aboutsummaryrefslogtreecommitdiff
path: root/src/ext/Util
diff options
context:
space:
mode:
Diffstat (limited to 'src/ext/Util')
-rw-r--r--src/ext/Util/CustomizedNativeRecommendedRules.ruleset8
-rw-r--r--src/ext/Util/Directory.Build.props27
-rw-r--r--src/ext/Util/Directory.Build.targets51
-rw-r--r--src/ext/Util/Directory.csproj.props13
-rw-r--r--src/ext/Util/Directory.csproj.targets26
-rw-r--r--src/ext/Util/Directory.vcxproj.props97
-rw-r--r--src/ext/Util/README.md3
-rw-r--r--src/ext/Util/Util.wixext.sln87
-rw-r--r--src/ext/Util/Util.wixext.v3.ncrunchsolution6
-rw-r--r--src/ext/Util/appveyor.cmd19
-rw-r--r--src/ext/Util/appveyor.yml40
-rw-r--r--src/ext/Util/be/UtilBundleExtension.cpp87
-rw-r--r--src/ext/Util/be/UtilBundleExtension.h16
-rw-r--r--src/ext/Util/be/beDecor.h13
-rw-r--r--src/ext/Util/be/detectsha2support.cpp58
-rw-r--r--src/ext/Util/be/detectsha2support.h8
-rw-r--r--src/ext/Util/be/precomp.cpp3
-rw-r--r--src/ext/Util/be/precomp.h37
-rw-r--r--src/ext/Util/be/utilbe.cpp41
-rw-r--r--src/ext/Util/be/utilbe.def8
-rw-r--r--src/ext/Util/be/utilbe.vcxproj80
-rw-r--r--src/ext/Util/be/utilsearch.cpp160
-rw-r--r--src/ext/Util/be/utilsearch.h65
-rw-r--r--src/ext/Util/ca/BroadcastSettingChange.cpp45
-rw-r--r--src/ext/Util/ca/CheckReboot.cpp36
-rw-r--r--src/ext/Util/ca/CloseApps.cpp568
-rw-r--r--src/ext/Util/ca/CustomMsiErrors.h32
-rw-r--r--src/ext/Util/ca/FormatFiles.cpp221
-rw-r--r--src/ext/Util/ca/OsInfo.cpp487
-rw-r--r--src/ext/Util/ca/RemoveFoldersEx.cpp243
-rw-r--r--src/ext/Util/ca/RemoveRegistryKeysEx.cpp114
-rw-r--r--src/ext/Util/ca/RestartManager.cpp185
-rw-r--r--src/ext/Util/ca/TouchFile.cpp308
-rw-r--r--src/ext/Util/ca/XmlConfig.cpp1130
-rw-r--r--src/ext/Util/ca/XmlFile.cpp940
-rw-r--r--src/ext/Util/ca/caDecor.h13
-rw-r--r--src/ext/Util/ca/cost.h9
-rw-r--r--src/ext/Util/ca/dllmain.cpp26
-rw-r--r--src/ext/Util/ca/exitearlywithsuccess.cpp27
-rw-r--r--src/ext/Util/ca/netshortcuts.cpp437
-rw-r--r--src/ext/Util/ca/precomp.h54
-rw-r--r--src/ext/Util/ca/qtexecca.cpp316
-rw-r--r--src/ext/Util/ca/sca.h19
-rw-r--r--src/ext/Util/ca/scacost.h18
-rw-r--r--src/ext/Util/ca/scaexec.cpp1082
-rw-r--r--src/ext/Util/ca/scamanifest.cpp377
-rw-r--r--src/ext/Util/ca/scaperf.cpp310
-rw-r--r--src/ext/Util/ca/scaperfexec.cpp423
-rw-r--r--src/ext/Util/ca/scasched.cpp127
-rw-r--r--src/ext/Util/ca/scasmb.h46
-rw-r--r--src/ext/Util/ca/scasmbexec.cpp316
-rw-r--r--src/ext/Util/ca/scasmbexec.h27
-rw-r--r--src/ext/Util/ca/scasmbsched.cpp639
-rw-r--r--src/ext/Util/ca/scauser.cpp709
-rw-r--r--src/ext/Util/ca/scauser.h67
-rw-r--r--src/ext/Util/ca/secureobj.cpp915
-rw-r--r--src/ext/Util/ca/serviceconfig.cpp821
-rw-r--r--src/ext/Util/ca/shellexecca.cpp271
-rw-r--r--src/ext/Util/ca/test.cpp269
-rw-r--r--src/ext/Util/ca/utilca.cpp3
-rw-r--r--src/ext/Util/ca/utilca.def91
-rw-r--r--src/ext/Util/ca/utilca.vcxproj106
-rw-r--r--src/ext/Util/nuget.config18
-rw-r--r--src/ext/Util/test/WixToolsetTest.Util/TestData/.Data/burn.exebin0 -> 463360 bytes
-rw-r--r--src/ext/Util/test/WixToolsetTest.Util/TestData/BundleWithSearches/Bundle.en-us.wxl8
-rw-r--r--src/ext/Util/test/WixToolsetTest.Util/TestData/BundleWithSearches/Bundle.wxs52
-rw-r--r--src/ext/Util/test/WixToolsetTest.Util/TestData/BundleWithSearches/data/MsiPackage/Shared.dll1
-rw-r--r--src/ext/Util/test/WixToolsetTest.Util/TestData/BundleWithSearches/data/MsiPackage/test.txt1
-rw-r--r--src/ext/Util/test/WixToolsetTest.Util/TestData/BundleWithSearches/data/fakeba.dll1
-rw-r--r--src/ext/Util/test/WixToolsetTest.Util/TestData/BundleWithSearches/data/test.msibin0 -> 32768 bytes
-rw-r--r--src/ext/Util/test/WixToolsetTest.Util/TestData/CloseApplication/Package.en-us.wxl9
-rw-r--r--src/ext/Util/test/WixToolsetTest.Util/TestData/CloseApplication/Package.wxs17
-rw-r--r--src/ext/Util/test/WixToolsetTest.Util/TestData/CloseApplication/PackageComponents.wxs9
-rw-r--r--src/ext/Util/test/WixToolsetTest.Util/TestData/CloseApplication/example.txt1
-rw-r--r--src/ext/Util/test/WixToolsetTest.Util/TestData/EventManifest/Package.en-us.wxl9
-rw-r--r--src/ext/Util/test/WixToolsetTest.Util/TestData/EventManifest/Package.wxs15
-rw-r--r--src/ext/Util/test/WixToolsetTest.Util/TestData/EventManifest/PackageComponents.wxs11
-rw-r--r--src/ext/Util/test/WixToolsetTest.Util/TestData/EventManifest/example.txt1
-rw-r--r--src/ext/Util/test/WixToolsetTest.Util/TestData/InternetShortcut/Package.en-us.wxl9
-rw-r--r--src/ext/Util/test/WixToolsetTest.Util/TestData/InternetShortcut/Package.icobin0 -> 83899 bytes
-rw-r--r--src/ext/Util/test/WixToolsetTest.Util/TestData/InternetShortcut/Package.wxs15
-rw-r--r--src/ext/Util/test/WixToolsetTest.Util/TestData/InternetShortcut/PackageComponents.wxs11
-rw-r--r--src/ext/Util/test/WixToolsetTest.Util/TestData/InternetShortcut/example.txt1
-rw-r--r--src/ext/Util/test/WixToolsetTest.Util/TestData/InternetShortcutModule/Module.wxs13
-rw-r--r--src/ext/Util/test/WixToolsetTest.Util/TestData/InternetShortcutModule/ModuleComponents.wxs11
-rw-r--r--src/ext/Util/test/WixToolsetTest.Util/TestData/InternetShortcutModule/Package.icobin0 -> 83899 bytes
-rw-r--r--src/ext/Util/test/WixToolsetTest.Util/TestData/PermissionEx/Package.en-us.wxl9
-rw-r--r--src/ext/Util/test/WixToolsetTest.Util/TestData/PermissionEx/Package.wxs15
-rw-r--r--src/ext/Util/test/WixToolsetTest.Util/TestData/PermissionEx/PackageComponents.wxs23
-rw-r--r--src/ext/Util/test/WixToolsetTest.Util/TestData/PermissionEx/example.txt1
-rw-r--r--src/ext/Util/test/WixToolsetTest.Util/TestData/Queries/Package.en-us.wxl9
-rw-r--r--src/ext/Util/test/WixToolsetTest.Util/TestData/Queries/Package.wxs23
-rw-r--r--src/ext/Util/test/WixToolsetTest.Util/TestData/Queries/PackageComponents.wxs9
-rw-r--r--src/ext/Util/test/WixToolsetTest.Util/TestData/Queries/example.txt1
-rw-r--r--src/ext/Util/test/WixToolsetTest.Util/TestData/RemoveFolderEx/Module.wxs13
-rw-r--r--src/ext/Util/test/WixToolsetTest.Util/TestData/RemoveFolderEx/ModuleComponents.wxs10
-rw-r--r--src/ext/Util/test/WixToolsetTest.Util/TestData/RemoveRegistryKeyEx/Module.wxs13
-rw-r--r--src/ext/Util/test/WixToolsetTest.Util/TestData/RemoveRegistryKeyEx/ModuleComponents.wxs10
-rw-r--r--src/ext/Util/test/WixToolsetTest.Util/TestData/UsingFileShare/Package.en-us.wxl9
-rw-r--r--src/ext/Util/test/WixToolsetTest.Util/TestData/UsingFileShare/Package.wxs15
-rw-r--r--src/ext/Util/test/WixToolsetTest.Util/TestData/UsingFileShare/PackageComponents.wxs14
-rw-r--r--src/ext/Util/test/WixToolsetTest.Util/TestData/UsingFileShare/example.txt1
-rw-r--r--src/ext/Util/test/WixToolsetTest.Util/TestData/XmlConfig/Package.en-us.wxl9
-rw-r--r--src/ext/Util/test/WixToolsetTest.Util/TestData/XmlConfig/Package.wxs17
-rw-r--r--src/ext/Util/test/WixToolsetTest.Util/TestData/XmlConfigModule/Module.wxs19
-rw-r--r--src/ext/Util/test/WixToolsetTest.Util/TestData/XmlConfigModule/my.xml1
-rw-r--r--src/ext/Util/test/WixToolsetTest.Util/UtilExtensionFixture.cs317
-rw-r--r--src/ext/Util/test/WixToolsetTest.Util/WixToolsetTest.Util.csproj38
-rw-r--r--src/ext/Util/test/WixToolsetTest.Util/WixToolsetTest.Util.v3.ncrunchproject5
-rw-r--r--src/ext/Util/wix.snkbin0 -> 596 bytes
-rw-r--r--src/ext/Util/wixext/PerformanceCounterType.cs192
-rw-r--r--src/ext/Util/wixext/Symbols/EventManifestSymbol.cs55
-rw-r--r--src/ext/Util/wixext/Symbols/FileSharePermissionsSymbol.cs63
-rw-r--r--src/ext/Util/wixext/Symbols/FileShareSymbol.cs71
-rw-r--r--src/ext/Util/wixext/Symbols/GroupSymbol.cs63
-rw-r--r--src/ext/Util/wixext/Symbols/PerfmonManifestSymbol.cs63
-rw-r--r--src/ext/Util/wixext/Symbols/PerfmonSymbol.cs63
-rw-r--r--src/ext/Util/wixext/Symbols/PerformanceCategorySymbol.cs71
-rw-r--r--src/ext/Util/wixext/Symbols/SecureObjectsSymbol.cs103
-rw-r--r--src/ext/Util/wixext/Symbols/ServiceConfigSymbol.cs119
-rw-r--r--src/ext/Util/wixext/Symbols/UserGroupSymbol.cs55
-rw-r--r--src/ext/Util/wixext/Symbols/UserSymbol.cs79
-rw-r--r--src/ext/Util/wixext/Symbols/UtilSymbolDefinitions.cs125
-rw-r--r--src/ext/Util/wixext/Symbols/WixCloseApplicationSymbol.cs103
-rw-r--r--src/ext/Util/wixext/Symbols/WixFormatFilesSymbol.cs55
-rw-r--r--src/ext/Util/wixext/Symbols/WixInternetShortcutSymbol.cs95
-rw-r--r--src/ext/Util/wixext/Symbols/WixRemoveFolderExSymbol.cs78
-rw-r--r--src/ext/Util/wixext/Symbols/WixRemoveRegistryKeyExSymbol.cs86
-rw-r--r--src/ext/Util/wixext/Symbols/WixRestartResourceSymbol.cs71
-rw-r--r--src/ext/Util/wixext/Symbols/WixTouchFileSymbol.cs63
-rw-r--r--src/ext/Util/wixext/Symbols/WixWindowsFeatureSearchSymbol.cs47
-rw-r--r--src/ext/Util/wixext/Symbols/XmlConfigSymbol.cs111
-rw-r--r--src/ext/Util/wixext/Symbols/XmlFileSymbol.cs95
-rw-r--r--src/ext/Util/wixext/UtilCompiler.cs3889
-rw-r--r--src/ext/Util/wixext/UtilConstants.cs17
-rw-r--r--src/ext/Util/wixext/UtilDecompiler.cs1543
-rw-r--r--src/ext/Util/wixext/UtilErrors.cs49
-rw-r--r--src/ext/Util/wixext/UtilExtensionData.cs23
-rw-r--r--src/ext/Util/wixext/UtilExtensionFactory.cs18
-rw-r--r--src/ext/Util/wixext/UtilTableDefinitions.cs319
-rw-r--r--src/ext/Util/wixext/UtilWarnings.cs37
-rw-r--r--src/ext/Util/wixext/UtilWindowsInstallerBackendExtension.cs13
-rw-r--r--src/ext/Util/wixext/WixToolset.Util.wixext.csproj31
-rw-r--r--src/ext/Util/wixext/WixToolset.Util.wixext.nuspec25
-rw-r--r--src/ext/Util/wixext/WixToolset.Util.wixext.targets11
-rw-r--r--src/ext/Util/wixext/WixToolset.Util.wixext.v3.ncrunchproject7
-rw-r--r--src/ext/Util/wixlib/UtilBundleExtension_Platform.wxi10
-rw-r--r--src/ext/Util/wixlib/UtilBundleExtension_arm64.wxs7
-rw-r--r--src/ext/Util/wixlib/UtilBundleExtension_x64.wxs7
-rw-r--r--src/ext/Util/wixlib/UtilBundleExtension_x86.wxs7
-rw-r--r--src/ext/Util/wixlib/UtilExtension.wxs64
-rw-r--r--src/ext/Util/wixlib/UtilExtension_Platform.wxi360
-rw-r--r--src/ext/Util/wixlib/UtilExtension_arm64.wxs7
-rw-r--r--src/ext/Util/wixlib/UtilExtension_x64.wxs7
-rw-r--r--src/ext/Util/wixlib/UtilExtension_x86.wxs7
-rw-r--r--src/ext/Util/wixlib/caDecor.wxi39
-rw-r--r--src/ext/Util/wixlib/caerr.wxi96
-rw-r--r--src/ext/Util/wixlib/de-de.wxl32
-rw-r--r--src/ext/Util/wixlib/en-us.wxl32
-rw-r--r--src/ext/Util/wixlib/es-es.wxl31
-rw-r--r--src/ext/Util/wixlib/fr-fr.wxl31
-rw-r--r--src/ext/Util/wixlib/it-it.wxl32
-rw-r--r--src/ext/Util/wixlib/ja-jp.wxl32
-rw-r--r--src/ext/Util/wixlib/pt-br.wxl26
-rw-r--r--src/ext/Util/wixlib/util.v3.ncrunchproject5
-rw-r--r--src/ext/Util/wixlib/util.wixproj27
166 files changed, 22341 insertions, 0 deletions
diff --git a/src/ext/Util/CustomizedNativeRecommendedRules.ruleset b/src/ext/Util/CustomizedNativeRecommendedRules.ruleset
new file mode 100644
index 00000000..142b141c
--- /dev/null
+++ b/src/ext/Util/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/ext/Util/Directory.Build.props b/src/ext/Util/Directory.Build.props
new file mode 100644
index 00000000..b3c6287c
--- /dev/null
+++ b/src/ext/Util/Directory.Build.props
@@ -0,0 +1,27 @@
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 <MSBuildWarningsAsMessages>MSB3246</MSBuildWarningsAsMessages>
12
13 <ProjectName Condition=" '$(ProjectName)' == '' ">$(MSBuildProjectName)</ProjectName>
14 <BaseOutputPath>$([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)..\build\))</BaseOutputPath>
15 <BaseIntermediateOutputPath>$(BaseOutputPath)obj\$(ProjectName)\</BaseIntermediateOutputPath>
16 <OutputPath>$(BaseOutputPath)$(Configuration)\</OutputPath>
17
18 <Authors>WiX Toolset Team</Authors>
19 <Company>WiX Toolset</Company>
20 <Copyright>Copyright (c) .NET Foundation and contributors. All rights reserved.</Copyright>
21 <PackageLicenseExpression>MS-RL</PackageLicenseExpression>
22 <Product>WiX Toolset</Product>
23 </PropertyGroup>
24
25 <Import Project="Directory$(MSBuildProjectExtension).props" Condition=" Exists('Directory$(MSBuildProjectExtension).props') " />
26 <Import Project="Custom.Build.props" Condition=" Exists('Custom.Build.props') " />
27</Project>
diff --git a/src/ext/Util/Directory.Build.targets b/src/ext/Util/Directory.Build.targets
new file mode 100644
index 00000000..2fcc765a
--- /dev/null
+++ b/src/ext/Util/Directory.Build.targets
@@ -0,0 +1,51 @@
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<!--
8 Replace PackageReferences with ProjectReferences when the projects can be found in .sln.
9 See the original here: https://github.com/dotnet/sdk/issues/1151#issuecomment-385133284
10-->
11<Project>
12 <PropertyGroup>
13 <ReplacePackageReferences>true</ReplacePackageReferences>
14 <TheSolutionPath Condition=" '$(NCrunch)'=='' ">$(SolutionPath)</TheSolutionPath>
15 <TheSolutionPath Condition=" '$(NCrunch)'=='1' ">$(NCrunchOriginalSolutionPath)</TheSolutionPath>
16 </PropertyGroup>
17
18 <Choose>
19 <When Condition="$(ReplacePackageReferences) AND '$(TheSolutionPath)' != '' AND '$(TheSolutionPath)' != '*undefined*' AND Exists('$(TheSolutionPath)')">
20
21 <PropertyGroup>
22 <SolutionFileContent>$([System.IO.File]::ReadAllText($(TheSolutionPath)))</SolutionFileContent>
23 <SmartSolutionDir>$([System.IO.Path]::GetDirectoryName( $(TheSolutionPath) ))</SmartSolutionDir>
24 <RegexPattern>(?&lt;="[PackageName]", ")(.*)(?=", ")</RegexPattern>
25 </PropertyGroup>
26
27 <ItemGroup>
28 <!-- Keep the identity of the PackageReference -->
29 <SmartPackageReference Include="@(PackageReference)">
30 <PackageName>%(Identity)</PackageName>
31 <InSolution>$(SolutionFileContent.Contains('\%(Identity).csproj'))</InSolution>
32 </SmartPackageReference>
33
34 <!-- Filter them by mapping them to another ItemGroup using the WithMetadataValue item function -->
35 <PackageInSolution Include="@(SmartPackageReference->WithMetadataValue('InSolution', True))">
36 <Pattern>$(RegexPattern.Replace('[PackageName]','%(PackageName)') )</Pattern>
37 <SmartPath>$([System.Text.RegularExpressions.Regex]::Match('$(SolutionFileContent)', '%(Pattern)'))</SmartPath>
38 </PackageInSolution>
39
40 <ProjectReference Include="@(PackageInSolution->'$(SmartSolutionDir)\%(SmartPath)' )"/>
41
42 <!-- Remove the package references that are now referenced as projects -->
43 <PackageReference Remove="@(PackageInSolution->'%(PackageName)')"/>
44 </ItemGroup>
45
46 </When>
47 </Choose>
48
49 <Import Project="Directory$(MSBuildProjectExtension).targets" Condition=" Exists('Directory$(MSBuildProjectExtension).targets') " />
50 <Import Project="Custom.Build.targets" Condition=" Exists('Custom.Build.targets') " />
51</Project>
diff --git a/src/ext/Util/Directory.csproj.props b/src/ext/Util/Directory.csproj.props
new file mode 100644
index 00000000..81d24ad1
--- /dev/null
+++ b/src/ext/Util/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/ext/Util/Directory.csproj.targets b/src/ext/Util/Directory.csproj.targets
new file mode 100644
index 00000000..c3270426
--- /dev/null
+++ b/src/ext/Util/Directory.csproj.targets
@@ -0,0 +1,26 @@
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\Directory.csproj.targets
4 then update all of the repos.
5-->
6<Project>
7 <PropertyGroup>
8 <CreateDocumentation Condition=" '$(CreateDocumentationFile)'!='true' ">false</CreateDocumentation>
9 <DocumentationFile Condition=" '$(CreateDocumentationFile)'=='true' ">$(OutputPath)\$(AssemblyName).xml</DocumentationFile>
10 </PropertyGroup>
11
12 <Target Name="SetNuspecProperties" DependsOnTargets="InitializeSourceControlInformation" AfterTargets="GetBuildVersion"
13 Condition=" Exists('$(MSBuildProjectName).nuspec') ">
14 <PropertyGroup>
15 <ProjectUrl Condition=" '$(ProjectUrl)'=='' and '$(PrivateRepositoryUrl)'!='' ">$(PrivateRepositoryUrl.Replace('.git',''))</ProjectUrl>
16
17 <NuspecFile>$(MSBuildProjectName).nuspec</NuspecFile>
18 <NuspecBasePath Condition=" '$(NuspecBasePath)'=='' ">$(OutputPath)..\</NuspecBasePath>
19 <NuspecProperties>$(NuspecProperties);Id=$(PackageId);Authors=$(Authors);Copyright=$(Copyright);Description=$(Description);Title=$(Title)</NuspecProperties>
20 <NuspecProperties>$(NuspecProperties);Version=$(PackageVersion);RepositoryCommit=$(SourceRevisionId);RepositoryType=$(RepositoryType);RepositoryUrl=$(PrivateRepositoryUrl);ProjectFolder=$(MSBuildProjectDirectory)\;ProjectUrl=$(ProjectUrl)</NuspecProperties>
21 <PublishRepositoryUrl>true</PublishRepositoryUrl>
22 <SymbolPackageFormat>snupkg</SymbolPackageFormat>
23 </PropertyGroup>
24 </Target>
25
26</Project>
diff --git a/src/ext/Util/Directory.vcxproj.props b/src/ext/Util/Directory.vcxproj.props
new file mode 100644
index 00000000..11778f41
--- /dev/null
+++ b/src/ext/Util/Directory.vcxproj.props
@@ -0,0 +1,97 @@
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>$(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=0x0501;$(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 <AdditionalOptions>-YlprecompDefine</AdditionalOptions>
38 <AdditionalOptions Condition=" $(PlatformToolset.StartsWith('v14')) ">/Zc:threadSafeInit- %(AdditionalOptions)</AdditionalOptions>
39 <MultiProcessorCompilation Condition=" $(NUMBER_OF_PROCESSORS) &gt; 4 ">true</MultiProcessorCompilation>
40 </ClCompile>
41 <ResourceCompile>
42 <PreprocessorDefinitions>$(ArmPreprocessorDefinitions);%(PreprocessorDefinitions)</PreprocessorDefinitions>
43 <AdditionalIncludeDirectories>$(ProjectAdditionalResourceIncludeDirectories);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
44 </ResourceCompile>
45 <Lib>
46 <AdditionalLibraryDirectories>$(OutDir);$(AdditionalMultiTargetLibraryPath);$(ProjectAdditionalLibraryDirectories);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
47 </Lib>
48 <Link>
49 <SubSystem>$(ProjectSubSystem)</SubSystem>
50 <ModuleDefinitionFile>$(ProjectModuleDefinitionFile)</ModuleDefinitionFile>
51 <NoEntryPoint>$(ResourceOnlyDll)</NoEntryPoint>
52 <GenerateDebugInformation>true</GenerateDebugInformation>
53 <AdditionalDependencies>$(ProjectAdditionalLinkLibraries);advapi32.lib;comdlg32.lib;user32.lib;oleaut32.lib;gdi32.lib;shell32.lib;ole32.lib;version.lib;%(AdditionalDependencies)</AdditionalDependencies>
54 <AdditionalLibraryDirectories>$(OutDir);$(AdditionalMultiTargetLibraryPath);$(ArmLibraryDirectories);$(ProjectAdditionalLinkLibraryDirectories);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
55 <AdditionalOptions Condition=" $(PlatformToolset.StartsWith('v14')) ">/IGNORE:4099 %(AdditionalOptions)</AdditionalOptions>
56 </Link>
57 </ItemDefinitionGroup>
58
59 <ItemDefinitionGroup Condition=" '$(Platform)'=='Win32' and '$(PlatformToolset)'!='v100'">
60 <ClCompile>
61 <EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
62 </ClCompile>
63 </ItemDefinitionGroup>
64 <ItemDefinitionGroup Condition=" '$(Platform)'=='arm' ">
65 <ClCompile>
66 <CallingConvention>CDecl</CallingConvention>
67 </ClCompile>
68 </ItemDefinitionGroup>
69 <ItemDefinitionGroup Condition=" '$(ConfigurationType)'=='StaticLibrary' ">
70 <ClCompile>
71 <DebugInformationFormat>OldStyle</DebugInformationFormat>
72 <OmitDefaultLibName>true</OmitDefaultLibName>
73 <IgnoreAllDefaultLibraries>true</IgnoreAllDefaultLibraries>
74 </ClCompile>
75 </ItemDefinitionGroup>
76 <ItemDefinitionGroup Condition=" '$(Configuration)'=='Debug' ">
77 <ClCompile>
78 <Optimization>Disabled</Optimization>
79 <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
80 <PreprocessorDefinitions>_DEBUG;DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
81 <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
82 </ClCompile>
83 </ItemDefinitionGroup>
84 <ItemDefinitionGroup Condition=" '$(Configuration)'=='Release' ">
85 <ClCompile>
86 <Optimization>MinSpace</Optimization>
87 <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
88 <FunctionLevelLinking>true</FunctionLevelLinking>
89 <IntrinsicFunctions>true</IntrinsicFunctions>
90 <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
91 </ClCompile>
92 <Link>
93 <EnableCOMDATFolding>true</EnableCOMDATFolding>
94 <OptimizeReferences>true</OptimizeReferences>
95 </Link>
96 </ItemDefinitionGroup>
97</Project>
diff --git a/src/ext/Util/README.md b/src/ext/Util/README.md
new file mode 100644
index 00000000..540c539c
--- /dev/null
+++ b/src/ext/Util/README.md
@@ -0,0 +1,3 @@
1# Util.wixext
2WixToolset.Util.wixext - Utility WiX Toolset Extension
3
diff --git a/src/ext/Util/Util.wixext.sln b/src/ext/Util/Util.wixext.sln
new file mode 100644
index 00000000..050fd8b3
--- /dev/null
+++ b/src/ext/Util/Util.wixext.sln
@@ -0,0 +1,87 @@
1
2Microsoft Visual Studio Solution File, Format Version 12.00
3# Visual Studio Version 16
4VisualStudioVersion = 16.0.30204.135
5MinimumVisualStudioVersion = 15.0.26124.0
6Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "utilbe", "src\be\utilbe.vcxproj", "{630C1EE7-2517-4A8C-83E3-DA1150308B58}"
7EndProject
8Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "utilca", "src\ca\utilca.vcxproj", "{076018F7-19BD-423A-ABBF-229273DA08D8}"
9EndProject
10Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "util", "src\wixlib\util.wixproj", "{1ACFFEFD-505A-41A5-ACBF-A02B7B473AA2}"
11EndProject
12Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolset.Util.wixext", "src\wixext\WixToolset.Util.wixext.csproj", "{6CF033EB-0A39-4AC6-9D41-9BD506352045}"
13EndProject
14Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolsetTest.Util", "src\test\WixToolsetTest.Util\WixToolsetTest.Util.csproj", "{D5D34EC4-AF91-4B11-AC0A-FA5242AE924B}"
15EndProject
16Global
17 GlobalSection(SolutionConfigurationPlatforms) = preSolution
18 Debug|Any CPU = Debug|Any CPU
19 Debug|x64 = Debug|x64
20 Debug|x86 = Debug|x86
21 Release|Any CPU = Release|Any CPU
22 Release|x64 = Release|x64
23 Release|x86 = Release|x86
24 EndGlobalSection
25 GlobalSection(ProjectConfigurationPlatforms) = postSolution
26 {630C1EE7-2517-4A8C-83E3-DA1150308B58}.Debug|Any CPU.ActiveCfg = Debug|Win32
27 {630C1EE7-2517-4A8C-83E3-DA1150308B58}.Debug|Any CPU.Build.0 = Debug|Win32
28 {630C1EE7-2517-4A8C-83E3-DA1150308B58}.Debug|x64.ActiveCfg = Debug|Win32
29 {630C1EE7-2517-4A8C-83E3-DA1150308B58}.Debug|x86.ActiveCfg = Debug|Win32
30 {630C1EE7-2517-4A8C-83E3-DA1150308B58}.Debug|x86.Build.0 = Debug|Win32
31 {630C1EE7-2517-4A8C-83E3-DA1150308B58}.Release|Any CPU.ActiveCfg = Release|Win32
32 {630C1EE7-2517-4A8C-83E3-DA1150308B58}.Release|Any CPU.Build.0 = Release|Win32
33 {630C1EE7-2517-4A8C-83E3-DA1150308B58}.Release|x64.ActiveCfg = Release|Win32
34 {630C1EE7-2517-4A8C-83E3-DA1150308B58}.Release|x86.ActiveCfg = Release|Win32
35 {630C1EE7-2517-4A8C-83E3-DA1150308B58}.Release|x86.Build.0 = Release|Win32
36 {076018F7-19BD-423A-ABBF-229273DA08D8}.Debug|Any CPU.ActiveCfg = Debug|Win32
37 {076018F7-19BD-423A-ABBF-229273DA08D8}.Debug|Any CPU.Build.0 = Debug|Win32
38 {076018F7-19BD-423A-ABBF-229273DA08D8}.Debug|x64.ActiveCfg = Debug|Win32
39 {076018F7-19BD-423A-ABBF-229273DA08D8}.Debug|x86.ActiveCfg = Debug|Win32
40 {076018F7-19BD-423A-ABBF-229273DA08D8}.Debug|x86.Build.0 = Debug|Win32
41 {076018F7-19BD-423A-ABBF-229273DA08D8}.Release|Any CPU.ActiveCfg = Release|Win32
42 {076018F7-19BD-423A-ABBF-229273DA08D8}.Release|Any CPU.Build.0 = Release|Win32
43 {076018F7-19BD-423A-ABBF-229273DA08D8}.Release|x64.ActiveCfg = Release|Win32
44 {076018F7-19BD-423A-ABBF-229273DA08D8}.Release|x86.ActiveCfg = Release|Win32
45 {076018F7-19BD-423A-ABBF-229273DA08D8}.Release|x86.Build.0 = Release|Win32
46 {1ACFFEFD-505A-41A5-ACBF-A02B7B473AA2}.Debug|Any CPU.ActiveCfg = Debug|x86
47 {1ACFFEFD-505A-41A5-ACBF-A02B7B473AA2}.Debug|Any CPU.Build.0 = Debug|x86
48 {1ACFFEFD-505A-41A5-ACBF-A02B7B473AA2}.Debug|x64.ActiveCfg = Debug|x86
49 {1ACFFEFD-505A-41A5-ACBF-A02B7B473AA2}.Debug|x86.ActiveCfg = Debug|x86
50 {1ACFFEFD-505A-41A5-ACBF-A02B7B473AA2}.Debug|x86.Build.0 = Debug|x86
51 {1ACFFEFD-505A-41A5-ACBF-A02B7B473AA2}.Release|Any CPU.ActiveCfg = Release|x86
52 {1ACFFEFD-505A-41A5-ACBF-A02B7B473AA2}.Release|Any CPU.Build.0 = Release|x86
53 {1ACFFEFD-505A-41A5-ACBF-A02B7B473AA2}.Release|x64.ActiveCfg = Release|x86
54 {1ACFFEFD-505A-41A5-ACBF-A02B7B473AA2}.Release|x86.ActiveCfg = Release|x86
55 {1ACFFEFD-505A-41A5-ACBF-A02B7B473AA2}.Release|x86.Build.0 = Release|x86
56 {6CF033EB-0A39-4AC6-9D41-9BD506352045}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
57 {6CF033EB-0A39-4AC6-9D41-9BD506352045}.Debug|Any CPU.Build.0 = Debug|Any CPU
58 {6CF033EB-0A39-4AC6-9D41-9BD506352045}.Debug|x64.ActiveCfg = Debug|Any CPU
59 {6CF033EB-0A39-4AC6-9D41-9BD506352045}.Debug|x64.Build.0 = Debug|Any CPU
60 {6CF033EB-0A39-4AC6-9D41-9BD506352045}.Debug|x86.ActiveCfg = Debug|Any CPU
61 {6CF033EB-0A39-4AC6-9D41-9BD506352045}.Debug|x86.Build.0 = Debug|Any CPU
62 {6CF033EB-0A39-4AC6-9D41-9BD506352045}.Release|Any CPU.ActiveCfg = Release|Any CPU
63 {6CF033EB-0A39-4AC6-9D41-9BD506352045}.Release|Any CPU.Build.0 = Release|Any CPU
64 {6CF033EB-0A39-4AC6-9D41-9BD506352045}.Release|x64.ActiveCfg = Release|Any CPU
65 {6CF033EB-0A39-4AC6-9D41-9BD506352045}.Release|x64.Build.0 = Release|Any CPU
66 {6CF033EB-0A39-4AC6-9D41-9BD506352045}.Release|x86.ActiveCfg = Release|Any CPU
67 {6CF033EB-0A39-4AC6-9D41-9BD506352045}.Release|x86.Build.0 = Release|Any CPU
68 {D5D34EC4-AF91-4B11-AC0A-FA5242AE924B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
69 {D5D34EC4-AF91-4B11-AC0A-FA5242AE924B}.Debug|Any CPU.Build.0 = Debug|Any CPU
70 {D5D34EC4-AF91-4B11-AC0A-FA5242AE924B}.Debug|x64.ActiveCfg = Debug|Any CPU
71 {D5D34EC4-AF91-4B11-AC0A-FA5242AE924B}.Debug|x64.Build.0 = Debug|Any CPU
72 {D5D34EC4-AF91-4B11-AC0A-FA5242AE924B}.Debug|x86.ActiveCfg = Debug|Any CPU
73 {D5D34EC4-AF91-4B11-AC0A-FA5242AE924B}.Debug|x86.Build.0 = Debug|Any CPU
74 {D5D34EC4-AF91-4B11-AC0A-FA5242AE924B}.Release|Any CPU.ActiveCfg = Release|Any CPU
75 {D5D34EC4-AF91-4B11-AC0A-FA5242AE924B}.Release|Any CPU.Build.0 = Release|Any CPU
76 {D5D34EC4-AF91-4B11-AC0A-FA5242AE924B}.Release|x64.ActiveCfg = Release|Any CPU
77 {D5D34EC4-AF91-4B11-AC0A-FA5242AE924B}.Release|x64.Build.0 = Release|Any CPU
78 {D5D34EC4-AF91-4B11-AC0A-FA5242AE924B}.Release|x86.ActiveCfg = Release|Any CPU
79 {D5D34EC4-AF91-4B11-AC0A-FA5242AE924B}.Release|x86.Build.0 = Release|Any CPU
80 EndGlobalSection
81 GlobalSection(SolutionProperties) = preSolution
82 HideSolutionNode = FALSE
83 EndGlobalSection
84 GlobalSection(ExtensibilityGlobals) = postSolution
85 SolutionGuid = {E4566A6B-47D0-4EA0-989A-D763AC39105D}
86 EndGlobalSection
87EndGlobal
diff --git a/src/ext/Util/Util.wixext.v3.ncrunchsolution b/src/ext/Util/Util.wixext.v3.ncrunchsolution
new file mode 100644
index 00000000..10420ac9
--- /dev/null
+++ b/src/ext/Util/Util.wixext.v3.ncrunchsolution
@@ -0,0 +1,6 @@
1<SolutionConfiguration>
2 <Settings>
3 <AllowParallelTestExecution>True</AllowParallelTestExecution>
4 <SolutionConfigured>True</SolutionConfigured>
5 </Settings>
6</SolutionConfiguration> \ No newline at end of file
diff --git a/src/ext/Util/appveyor.cmd b/src/ext/Util/appveyor.cmd
new file mode 100644
index 00000000..8322ffae
--- /dev/null
+++ b/src/ext/Util/appveyor.cmd
@@ -0,0 +1,19 @@
1@setlocal
2@pushd %~dp0
3@set _C=Release
4@if /i "%1"=="debug" set _C=Debug
5
6:: Restore
7msbuild -p:Configuration=%_C% -t:Restore || exit /b
8
9:: Build
10msbuild -p:Configuration=%_C% src\test\WixToolsetTest.Util\WixToolsetTest.Util.csproj || exit /b
11
12:: Test
13dotnet test -c %_C% --no-build src\test\WixToolsetTest.Util || exit /b
14
15:: Pack
16msbuild -p:Configuration=%_C% -p:NoBuild=true -t:Pack src\wixext\WixToolset.Util.wixext.csproj || exit /b
17
18@popd
19@endlocal
diff --git a/src/ext/Util/appveyor.yml b/src/ext/Util/appveyor.yml
new file mode 100644
index 00000000..7c686b04
--- /dev/null
+++ b/src/ext/Util/appveyor.yml
@@ -0,0 +1,40 @@
1# Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2#
3# Do NOT modify this file. Update the canonical version in Home\repo-template\src\appveyor.yml
4# then update all of the repos.
5
6branches:
7 only:
8 - master
9 - develop
10
11image: Visual Studio 2019
12
13version: 0.0.0.{build}
14configuration: Release
15
16environment:
17 DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
18 DOTNET_CLI_TELEMETRY_OPTOUT: 1
19 NUGET_XMLDOC_MODE: skip
20
21build_script:
22 - appveyor.cmd
23
24pull_requests:
25 do_not_increment_build_number: true
26
27nuget:
28 disable_publish_on_pr: true
29
30skip_branch_with_pr: true
31skip_tags: true
32
33artifacts:
34- path: build\Release\**\*.nupkg
35 name: nuget
36
37notifications:
38- provider: Slack
39 incoming_webhook:
40 secure: p5xuu+4x2JHfwGDMDe5KcG1k7gZxqYc4jWVwvyNZv5cvkubPD2waJs5yXMAXZNN7Z63/3PWHb7q4KoY/99AjauYa1nZ4c5qYqRPFRBKTHfA=
diff --git a/src/ext/Util/be/UtilBundleExtension.cpp b/src/ext/Util/be/UtilBundleExtension.cpp
new file mode 100644
index 00000000..2ac842a5
--- /dev/null
+++ b/src/ext/Util/be/UtilBundleExtension.cpp
@@ -0,0 +1,87 @@
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#include "BextBaseBundleExtension.h"
5
6class CWixUtilBundleExtension : public CBextBaseBundleExtension
7{
8public: // IBundleExtension
9 virtual STDMETHODIMP Search(
10 __in LPCWSTR wzId,
11 __in LPCWSTR wzVariable
12 )
13 {
14 HRESULT hr = S_OK;
15
16 hr = UtilSearchExecute(&m_searches, wzId, wzVariable, m_pEngine);
17
18 return hr;
19 }
20
21public: //CBextBaseBundleExtension
22 virtual STDMETHODIMP Initialize(
23 __in const BUNDLE_EXTENSION_CREATE_ARGS* pCreateArgs
24 )
25 {
26 HRESULT hr = S_OK;
27 IXMLDOMDocument* pixdManifest = NULL;
28 IXMLDOMNode* pixnBundleExtension = NULL;
29
30 hr = CBextBaseBundleExtension::Initialize(pCreateArgs);
31 ExitOnFailure(hr, "CBextBaseBundleExtension initialization failed.");
32
33 hr = XmlLoadDocumentFromFile(m_sczBundleExtensionDataPath, &pixdManifest);
34 ExitOnFailure(hr, "Failed to load bundle extension manifest from path: %ls", m_sczBundleExtensionDataPath);
35
36 hr = BextGetBundleExtensionDataNode(pixdManifest, UTIL_BUNDLE_EXTENSION_ID, &pixnBundleExtension);
37 ExitOnFailure(hr, "Failed to get BundleExtension '%ls'", UTIL_BUNDLE_EXTENSION_ID);
38
39 hr = UtilSearchParseFromXml(&m_searches, pixnBundleExtension);
40 ExitOnFailure(hr, "Failed to parse searches from bundle extension manifest.");
41
42 LExit:
43 ReleaseObject(pixnBundleExtension);
44 ReleaseObject(pixdManifest);
45
46 return hr;
47 }
48
49public:
50 CWixUtilBundleExtension(
51 __in IBundleExtensionEngine* pEngine
52 ) : CBextBaseBundleExtension(pEngine)
53 {
54 m_searches = { };
55 }
56
57 ~CWixUtilBundleExtension()
58 {
59 UtilSearchUninitialize(&m_searches);
60 }
61
62private:
63 UTIL_SEARCHES m_searches;
64};
65
66HRESULT UtilBundleExtensionCreate(
67 __in IBundleExtensionEngine* pEngine,
68 __in const BUNDLE_EXTENSION_CREATE_ARGS* pArgs,
69 __out IBundleExtension** ppBundleExtension
70 )
71{
72 HRESULT hr = S_OK;
73 CWixUtilBundleExtension* pExtension = NULL;
74
75 pExtension = new CWixUtilBundleExtension(pEngine);
76 ExitOnNull(pExtension, hr, E_OUTOFMEMORY, "Failed to create new CWixUtilBundleExtension.");
77
78 hr = pExtension->Initialize(pArgs);
79 ExitOnFailure(hr, "CWixUtilBundleExtension initialization failed");
80
81 *ppBundleExtension = pExtension;
82 pExtension = NULL;
83
84LExit:
85 ReleaseObject(pExtension);
86 return hr;
87}
diff --git a/src/ext/Util/be/UtilBundleExtension.h b/src/ext/Util/be/UtilBundleExtension.h
new file mode 100644
index 00000000..c55d6b85
--- /dev/null
+++ b/src/ext/Util/be/UtilBundleExtension.h
@@ -0,0 +1,16 @@
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// constants
6
7#define UTIL_BUNDLE_EXTENSION_ID BUNDLE_EXTENSION_DECORATION(L"UtilBundleExtension")
8
9
10// function declarations
11
12HRESULT UtilBundleExtensionCreate(
13 __in IBundleExtensionEngine* pEngine,
14 __in const BUNDLE_EXTENSION_CREATE_ARGS* pArgs,
15 __out IBundleExtension** ppBundleExtension
16 );
diff --git a/src/ext/Util/be/beDecor.h b/src/ext/Util/be/beDecor.h
new file mode 100644
index 00000000..2c6a8818
--- /dev/null
+++ b/src/ext/Util/be/beDecor.h
@@ -0,0 +1,13 @@
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(_M_ARM64)
6#define BUNDLE_EXTENSION_DECORATION(f) L"Wix4" f L"_A64"
7#elif defined(_M_AMD64)
8#define BUNDLE_EXTENSION_DECORATION(f) L"Wix4" f L"_X64"
9#elif defined(_M_ARM)
10#define BUNDLE_EXTENSION_DECORATION(f) L"Wix4" f L"_ARM"
11#else
12#define BUNDLE_EXTENSION_DECORATION(f) L"Wix4" f L"_X86"
13#endif
diff --git a/src/ext/Util/be/detectsha2support.cpp b/src/ext/Util/be/detectsha2support.cpp
new file mode 100644
index 00000000..90e349cd
--- /dev/null
+++ b/src/ext/Util/be/detectsha2support.cpp
@@ -0,0 +1,58 @@
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// https://gist.github.com/navossoc/7572c7d82243e9f818989e2765e7793a
6HRESULT DetectSHA2CodeSigning(
7 __out BOOL* pfSupported
8 )
9{
10 HRESULT hr = S_OK;
11 HMODULE hModule = NULL;
12 FARPROC pfn = NULL;
13 DWORD er = ERROR_SUCCESS;
14
15 hr = LoadSystemLibrary(L"wintrust.dll", &hModule);
16 ExitOnFailure(hr, "Failed to load wintrust.dll");
17
18 pfn = ::GetProcAddress(hModule, "CryptCATAdminAcquireContext2");
19 if (pfn)
20 {
21 *pfSupported = TRUE;
22 ExitFunction1(hr = S_OK);
23 }
24
25 er = ::GetLastError();
26 if (er == ERROR_PROC_NOT_FOUND)
27 {
28 *pfSupported = FALSE;
29 ExitFunction1(hr = S_OK);
30 }
31
32 hr = HRESULT_FROM_WIN32(er);
33 ExitOnFailure(hr, "Failed to probe for CryptCATAdminAcquireContext2 in wintrust.dll");
34
35LExit:
36 ::FreeLibrary(hModule);
37
38 return hr;
39}
40
41HRESULT UtilPerformDetectSHA2CodeSigning(
42 __in LPCWSTR wzVariable,
43 __in UTIL_SEARCH* /*pSearch*/,
44 __in IBundleExtensionEngine* pEngine
45 )
46{
47 HRESULT hr = S_OK;
48 BOOL fSupported = FALSE;
49
50 hr = DetectSHA2CodeSigning(&fSupported);
51 ExitOnFailure(hr, "DetectSHA2CodeSigning failed.");
52
53 hr = pEngine->SetVariableNumeric(wzVariable, fSupported ? 1 : 0);
54 ExitOnFailure(hr, "Failed to set variable '%ls'", wzVariable);
55
56LExit:
57 return hr;
58}
diff --git a/src/ext/Util/be/detectsha2support.h b/src/ext/Util/be/detectsha2support.h
new file mode 100644
index 00000000..c38a3d59
--- /dev/null
+++ b/src/ext/Util/be/detectsha2support.h
@@ -0,0 +1,8 @@
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
4HRESULT UtilPerformDetectSHA2CodeSigning(
5 __in LPCWSTR wzVariable,
6 __in UTIL_SEARCH* pSearch,
7 __in IBundleExtensionEngine* pEngine
8 ); \ No newline at end of file
diff --git a/src/ext/Util/be/precomp.cpp b/src/ext/Util/be/precomp.cpp
new file mode 100644
index 00000000..37664a1c
--- /dev/null
+++ b/src/ext/Util/be/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/ext/Util/be/precomp.h b/src/ext/Util/be/precomp.h
new file mode 100644
index 00000000..76d24c7b
--- /dev/null
+++ b/src/ext/Util/be/precomp.h
@@ -0,0 +1,37 @@
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 _WIN32_MSI < 150
6#define _WIN32_MSI 150
7#endif
8
9#include <windows.h>
10#include <msiquery.h>
11#include <msidefs.h>
12#include <stierr.h>
13
14#include <strsafe.h>
15
16#include <msxml2.h>
17
18#define MAXUINT USHRT_MAX
19
20#include <dutil.h>
21#include <memutil.h>
22#include <strutil.h>
23#include <pathutil.h>
24#include <xmlutil.h>
25
26#include <BundleExtensionEngine.h>
27#include <BundleExtension.h>
28
29#include <IBundleExtensionEngine.h>
30#include <IBundleExtension.h>
31#include <bextutil.h>
32#include <BextBundleExtensionEngine.h>
33
34#include "beDecor.h"
35#include "utilsearch.h"
36#include "detectsha2support.h"
37#include "UtilBundleExtension.h"
diff --git a/src/ext/Util/be/utilbe.cpp b/src/ext/Util/be/utilbe.cpp
new file mode 100644
index 00000000..d9816dc7
--- /dev/null
+++ b/src/ext/Util/be/utilbe.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#include "BextBaseBundleExtensionProc.h"
5
6static IBundleExtension* vpBundleExtension = NULL;
7
8// function definitions
9
10extern "C" HRESULT WINAPI BundleExtensionCreate(
11 __in const BUNDLE_EXTENSION_CREATE_ARGS* pArgs,
12 __inout BUNDLE_EXTENSION_CREATE_RESULTS* pResults
13 )
14{
15 HRESULT hr = S_OK;
16 IBundleExtensionEngine* pEngine = NULL;
17
18 hr = XmlInitialize();
19 ExitOnFailure(hr, "Failed to initialize XML.");
20
21 hr = BextInitializeFromCreateArgs(pArgs, &pEngine);
22 ExitOnFailure(hr, "Failed to initialize bext");
23
24 hr = UtilBundleExtensionCreate(pEngine, pArgs, &vpBundleExtension);
25 BextExitOnFailure(hr, "Failed to create WixUtilBundleExtension");
26
27 pResults->pfnBundleExtensionProc = BextBaseBundleExtensionProc;
28 pResults->pvBundleExtensionProcContext = vpBundleExtension;
29
30LExit:
31 ReleaseObject(pEngine);
32
33 return hr;
34}
35
36extern "C" void WINAPI BundleExtensionDestroy()
37{
38 BextUninitialize();
39 ReleaseNullObject(vpBundleExtension);
40 XmlUninitialize();
41} \ No newline at end of file
diff --git a/src/ext/Util/be/utilbe.def b/src/ext/Util/be/utilbe.def
new file mode 100644
index 00000000..711b1a5c
--- /dev/null
+++ b/src/ext/Util/be/utilbe.def
@@ -0,0 +1,8 @@
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
4LIBRARY "utilbe"
5
6EXPORTS
7 BundleExtensionCreate
8 BundleExtensionDestroy
diff --git a/src/ext/Util/be/utilbe.vcxproj b/src/ext/Util/be/utilbe.vcxproj
new file mode 100644
index 00000000..683b376a
--- /dev/null
+++ b/src/ext/Util/be/utilbe.vcxproj
@@ -0,0 +1,80 @@
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="16.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
5 <ItemGroup Label="ProjectConfigurations">
6 <ProjectConfiguration Include="Debug|ARM64">
7 <Configuration>Debug</Configuration>
8 <Platform>ARM64</Platform>
9 </ProjectConfiguration>
10 <ProjectConfiguration Include="Release|ARM64">
11 <Configuration>Release</Configuration>
12 <Platform>ARM64</Platform>
13 </ProjectConfiguration>
14 <ProjectConfiguration Include="Debug|X64">
15 <Configuration>Debug</Configuration>
16 <Platform>X64</Platform>
17 </ProjectConfiguration>
18 <ProjectConfiguration Include="Release|X64">
19 <Configuration>Release</Configuration>
20 <Platform>X64</Platform>
21 </ProjectConfiguration>
22 <ProjectConfiguration Include="Debug|Win32">
23 <Configuration>Debug</Configuration>
24 <Platform>Win32</Platform>
25 </ProjectConfiguration>
26 <ProjectConfiguration Include="Release|Win32">
27 <Configuration>Release</Configuration>
28 <Platform>Win32</Platform>
29 </ProjectConfiguration>
30 </ItemGroup>
31
32 <PropertyGroup Label="Globals">
33 <ProjectGuid>{630C1EE7-2517-4A8C-83E3-DA1150308B58}</ProjectGuid>
34 <ConfigurationType>DynamicLibrary</ConfigurationType>
35 <TargetName>utilbe</TargetName>
36 <PlatformToolset>v142</PlatformToolset>
37 <CharacterSet>Unicode</CharacterSet>
38 <ProjectModuleDefinitionFile>utilbe.def</ProjectModuleDefinitionFile>
39 <Description>WiX Toolset Util BundleExtension</Description>
40 </PropertyGroup>
41
42 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
43 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
44
45 <PropertyGroup>
46 <ProjectAdditionalLinkLibraries>msi.lib</ProjectAdditionalLinkLibraries>
47 </PropertyGroup>
48
49 <ItemGroup>
50 <ClCompile Include="detectsha2support.cpp" />
51 <ClCompile Include="precomp.cpp">
52 <PrecompiledHeader>Create</PrecompiledHeader>
53 </ClCompile>
54 <ClCompile Include="utilbe.cpp" />
55 <ClCompile Include="UtilBundleExtension.cpp" />
56 <ClCompile Include="utilsearch.cpp" />
57 </ItemGroup>
58
59 <ItemGroup>
60 <ClInclude Include="beDecor.h" />
61 <ClInclude Include="detectsha2support.h" />
62 <ClInclude Include="precomp.h" />
63 <ClInclude Include="UtilBundleExtension.h" />
64 <ClInclude Include="utilsearch.h" />
65 </ItemGroup>
66
67 <ItemGroup>
68 <None Include="utilbe.def" />
69 </ItemGroup>
70
71 <ItemGroup>
72 <PackageReference Include="WixToolset.BextUtil" Version="4.0.58" />
73 <PackageReference Include="WixToolset.BootstrapperCore.Native" Version="4.0.141" />
74 <PackageReference Include="WixToolset.DUtil" Version="4.0.72" />
75 <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" />
76 <PackageReference Include="Nerdbank.GitVersioning" Version="3.3.37" />
77 </ItemGroup>
78
79 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
80</Project>
diff --git a/src/ext/Util/be/utilsearch.cpp b/src/ext/Util/be/utilsearch.cpp
new file mode 100644
index 00000000..7cd2ea09
--- /dev/null
+++ b/src/ext/Util/be/utilsearch.cpp
@@ -0,0 +1,160 @@
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
6STDMETHODIMP UtilSearchParseFromXml(
7 __in UTIL_SEARCHES* pSearches,
8 __in IXMLDOMNode* pixnBundleExtension
9 )
10{
11 HRESULT hr = S_OK;
12 IXMLDOMNodeList* pixnNodes = NULL;
13 IXMLDOMNode* pixnNode = NULL;
14 DWORD cNodes = 0;
15 BSTR bstrNodeName = NULL;
16 LPWSTR scz = NULL;
17
18 // Select Util search nodes.
19 hr = XmlSelectNodes(pixnBundleExtension, L"WixWindowsFeatureSearch", &pixnNodes);
20 ExitOnFailure(hr, "Failed to select Util search nodes.");
21
22 // Get Util search node count.
23 hr = pixnNodes->get_length((long*)&cNodes);
24 ExitOnFailure(hr, "Failed to get Util search node count.");
25
26 if (!cNodes)
27 {
28 ExitFunction();
29 }
30
31 // Allocate memory for searches.
32 pSearches->rgSearches = (UTIL_SEARCH*)MemAlloc(sizeof(UTIL_SEARCH) * cNodes, TRUE);
33 ExitOnNull(pSearches->rgSearches, hr, E_OUTOFMEMORY, "Failed to allocate memory for search structs.");
34
35 pSearches->cSearches = cNodes;
36
37 // Parse search elements.
38 for (DWORD i = 0; i < cNodes; ++i)
39 {
40 UTIL_SEARCH* pSearch = &pSearches->rgSearches[i];
41
42 hr = XmlNextElement(pixnNodes, &pixnNode, &bstrNodeName);
43 ExitOnFailure(hr, "Failed to get next node.");
44
45 // @Id
46 hr = XmlGetAttributeEx(pixnNode, L"Id", &pSearch->sczId);
47 ExitOnFailure(hr, "Failed to get @Id.");
48
49 // Read type specific attributes.
50 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"WixWindowsFeatureSearch", -1))
51 {
52 pSearch->Type = UTIL_SEARCH_TYPE_WINDOWS_FEATURE_SEARCH;
53
54 // @Type
55 hr = XmlGetAttributeEx(pixnNode, L"Type", &scz);
56 ExitOnFailure(hr, "Failed to get @Type.");
57
58 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"sha2CodeSigning", -1))
59 {
60 pSearch->WindowsFeatureSearch.type = UTIL_WINDOWS_FEATURE_SEARCH_TYPE_SHA2_CODE_SIGNING;
61 }
62 else
63 {
64 hr = E_INVALIDARG;
65 ExitOnFailure(hr, "Invalid value for @Type: %ls", scz);
66 }
67 }
68 else
69 {
70 hr = E_UNEXPECTED;
71 ExitOnFailure(hr, "Unexpected element name: %ls", bstrNodeName);
72 }
73
74 // prepare next iteration
75 ReleaseNullObject(pixnNode);
76 ReleaseNullBSTR(bstrNodeName);
77 }
78
79LExit:
80 ReleaseStr(scz);
81 ReleaseBSTR(bstrNodeName);
82 ReleaseObject(pixnNode);
83 ReleaseObject(pixnNodes);
84
85 return hr;
86}
87
88void UtilSearchUninitialize(
89 __in UTIL_SEARCHES* pSearches
90 )
91{
92 if (pSearches->rgSearches)
93 {
94 for (DWORD i = 0; i < pSearches->cSearches; ++i)
95 {
96 UTIL_SEARCH* pSearch = &pSearches->rgSearches[i];
97
98 ReleaseStr(pSearch->sczId);
99 }
100 MemFree(pSearches->rgSearches);
101 }
102}
103
104STDMETHODIMP UtilSearchExecute(
105 __in UTIL_SEARCHES* pSearches,
106 __in LPCWSTR wzSearchId,
107 __in LPCWSTR wzVariable,
108 __in IBundleExtensionEngine* pEngine
109 )
110{
111 HRESULT hr = S_OK;
112 UTIL_SEARCH* pSearch = NULL;
113
114 hr = UtilSearchFindById(pSearches, wzSearchId, &pSearch);
115 ExitOnFailure(hr, "Search id '%ls' is unknown to the util extension.");
116
117 switch (pSearch->Type)
118 {
119 case UTIL_SEARCH_TYPE_WINDOWS_FEATURE_SEARCH:
120 switch (pSearch->WindowsFeatureSearch.type)
121 {
122 case UTIL_WINDOWS_FEATURE_SEARCH_TYPE_SHA2_CODE_SIGNING:
123 hr = UtilPerformDetectSHA2CodeSigning(wzVariable, pSearch, pEngine);
124 break;
125 default:
126 hr = E_UNEXPECTED;
127 }
128 break;
129 default:
130 hr = E_UNEXPECTED;
131 }
132
133LExit:
134 return hr;
135}
136
137STDMETHODIMP UtilSearchFindById(
138 __in UTIL_SEARCHES* pSearches,
139 __in LPCWSTR wzId,
140 __out UTIL_SEARCH** ppSearch
141 )
142{
143 HRESULT hr = S_OK;
144
145 for (DWORD i = 0; i < pSearches->cSearches; ++i)
146 {
147 UTIL_SEARCH* pSearch = &pSearches->rgSearches[i];
148
149 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pSearch->sczId, -1, wzId, -1))
150 {
151 *ppSearch = pSearch;
152 ExitFunction1(hr = S_OK);
153 }
154 }
155
156 hr = E_NOTFOUND;
157
158LExit:
159 return hr;
160}
diff --git a/src/ext/Util/be/utilsearch.h b/src/ext/Util/be/utilsearch.h
new file mode 100644
index 00000000..deeab1f7
--- /dev/null
+++ b/src/ext/Util/be/utilsearch.h
@@ -0,0 +1,65 @@
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// constants
6
7enum UTIL_SEARCH_TYPE
8{
9 UTIL_SEARCH_TYPE_NONE,
10 UTIL_SEARCH_TYPE_WINDOWS_FEATURE_SEARCH,
11};
12
13enum UTIL_WINDOWS_FEATURE_SEARCH_TYPE
14{
15 UTIL_WINDOWS_FEATURE_SEARCH_TYPE_NONE,
16 UTIL_WINDOWS_FEATURE_SEARCH_TYPE_SHA2_CODE_SIGNING,
17};
18
19
20// structs
21
22typedef struct _UTIL_SEARCH
23{
24 LPWSTR sczId;
25
26 UTIL_SEARCH_TYPE Type;
27 union
28 {
29 struct
30 {
31 UTIL_WINDOWS_FEATURE_SEARCH_TYPE type;
32 } WindowsFeatureSearch;
33 };
34} UTIL_SEARCH;
35
36typedef struct _UTIL_SEARCHES
37{
38 UTIL_SEARCH* rgSearches;
39 DWORD cSearches;
40} UTIL_SEARCHES;
41
42
43// function declarations
44
45STDMETHODIMP UtilSearchParseFromXml(
46 __in UTIL_SEARCHES* pSearches,
47 __in IXMLDOMNode* pixnBundleExtension
48 );
49
50void UtilSearchUninitialize(
51 __in UTIL_SEARCHES* pSearches
52 );
53
54STDMETHODIMP UtilSearchExecute(
55 __in UTIL_SEARCHES* pSearches,
56 __in LPCWSTR wzSearchId,
57 __in LPCWSTR wzVariable,
58 __in IBundleExtensionEngine* pEngine
59 );
60
61STDMETHODIMP UtilSearchFindById(
62 __in UTIL_SEARCHES* pSearches,
63 __in LPCWSTR wzId,
64 __out UTIL_SEARCH** ppSearch
65 );
diff --git a/src/ext/Util/ca/BroadcastSettingChange.cpp b/src/ext/Util/ca/BroadcastSettingChange.cpp
new file mode 100644
index 00000000..2e153ad3
--- /dev/null
+++ b/src/ext/Util/ca/BroadcastSettingChange.cpp
@@ -0,0 +1,45 @@
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/********************************************************************
7WixBroadcastSettingChange
8
9 Send WM_SETTINGCHANGE message to all top-level windows indicating
10 that unspecified settings have changed.
11********************************************************************/
12extern "C" UINT __stdcall WixBroadcastSettingChange(
13 __in MSIHANDLE hInstall
14 )
15{
16 HRESULT hr = WcaInitialize(hInstall, "WixBroadcastSettingChange");
17 ExitOnFailure(hr, "failed to initialize WixBroadcastSettingChange");
18
19 // best effort; ignore failures
20 ::SendMessageTimeoutW(HWND_BROADCAST, WM_SETTINGCHANGE, NULL, NULL, SMTO_ABORTIFHUNG, 1000, NULL);
21
22LExit:
23 return WcaFinalize(ERROR_SUCCESS);
24}
25
26
27/********************************************************************
28WixBroadcastEnvironmentChange
29
30 Send WM_SETTINGCHANGE message to all top-level windows indicating
31 that environment variables have changed.
32********************************************************************/
33extern "C" UINT __stdcall WixBroadcastEnvironmentChange(
34 __in MSIHANDLE hInstall
35 )
36{
37 HRESULT hr = WcaInitialize(hInstall, "WixBroadcastEnvironmentChange");
38 ExitOnFailure(hr, "failed to initialize WixBroadcastEnvironmentChange");
39
40 // best effort; ignore failures
41 ::SendMessageTimeoutW(HWND_BROADCAST, WM_SETTINGCHANGE, NULL, reinterpret_cast<LPARAM>(L"Environment"), SMTO_ABORTIFHUNG, 1000, NULL);
42
43LExit:
44 return WcaFinalize(ERROR_SUCCESS);
45}
diff --git a/src/ext/Util/ca/CheckReboot.cpp b/src/ext/Util/ca/CheckReboot.cpp
new file mode 100644
index 00000000..ce056411
--- /dev/null
+++ b/src/ext/Util/ca/CheckReboot.cpp
@@ -0,0 +1,36 @@
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/********************************************************************
7WixCheckRebootRequired - entry point for WixCheckRebootRequired Custom Action
8
9 called as Type 1 CustomAction (binary DLL) from Windows Installer
10 in InstallExecuteSequence after InstallFinalize
11********************************************************************/
12extern "C" UINT __stdcall WixCheckRebootRequired(
13 __in MSIHANDLE hInstall
14 )
15{
16 HRESULT hr = S_OK;
17 DWORD er = ERROR_SUCCESS;
18
19 hr = WcaInitialize(hInstall, "WixCheckRebootRequired");
20 ExitOnFailure(hr, "failed to initialize");
21
22 if (WcaDidDeferredActionRequireReboot())
23 {
24 WcaLog(LOGMSG_STANDARD, "Reboot required by deferred CustomAction.");
25
26 er = ::MsiSetMode(hInstall, MSIRUNMODE_REBOOTATEND, TRUE);
27 hr = HRESULT_FROM_WIN32(er);
28 ExitOnFailure(hr, "Failed to schedule reboot.");
29 }
30
31LExit:
32
33 if (FAILED(hr))
34 er = ERROR_INSTALL_FAILURE;
35 return WcaFinalize(er);
36}
diff --git a/src/ext/Util/ca/CloseApps.cpp b/src/ext/Util/ca/CloseApps.cpp
new file mode 100644
index 00000000..d4256c43
--- /dev/null
+++ b/src/ext/Util/ca/CloseApps.cpp
@@ -0,0 +1,568 @@
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 DEFAULT_PROCESS_EXIT_WAIT_TIME 5000
6
7// structs
8LPCWSTR wzQUERY_CLOSEAPPS = L"SELECT `Wix4CloseApplication`, `Target`, `Description`, `Condition`, `Attributes`, `Property`, `TerminateExitCode`, `Timeout` FROM `Wix4CloseApplication` ORDER BY `Sequence`";
9enum eQUERY_CLOSEAPPS { QCA_ID = 1, QCA_TARGET, QCA_DESCRIPTION, QCA_CONDITION, QCA_ATTRIBUTES, QCA_PROPERTY, QCA_TERMINATEEXITCODE, QCA_TIMEOUT };
10
11// CloseApplication.Attributes
12enum CLOSEAPP_ATTRIBUTES
13{
14 CLOSEAPP_ATTRIBUTE_NONE = 0x0,
15 CLOSEAPP_ATTRIBUTE_CLOSEMESSAGE = 0x1,
16 CLOSEAPP_ATTRIBUTE_REBOOTPROMPT = 0x2,
17 CLOSEAPP_ATTRIBUTE_ELEVATEDCLOSEMESSAGE = 0x4,
18 CLOSEAPP_ATTRIBUTE_ENDSESSIONMESSAGE = 0x8,
19 CLOSEAPP_ATTRIBUTE_ELEVATEDENDSESSIONMESSAGE = 0x10,
20 CLOSEAPP_ATTRIBUTE_TERMINATEPROCESS = 0x20,
21 CLOSEAPP_ATTRIBUTE_PROMPTTOCONTINUE = 0x40,
22};
23
24struct PROCESS_AND_MESSAGE
25{
26 DWORD dwProcessId;
27 DWORD dwMessageId;
28 DWORD dwTimeout;
29};
30
31
32/******************************************************************
33 EnumWindowsProc - callback function which sends message if the
34 current window matches the passed in process ID
35
36******************************************************************/
37BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
38{
39 PROCESS_AND_MESSAGE* pPM = reinterpret_cast<PROCESS_AND_MESSAGE*>(lParam);
40 DWORD dwProcessId = 0;
41 DWORD_PTR dwResult = 0;
42 BOOL fQueryEndSession = WM_QUERYENDSESSION == pPM->dwMessageId;
43 BOOL fContinueWindowsInProcess = TRUE; // assume we will send message to all top-level windows in a process.
44
45 ::GetWindowThreadProcessId(hwnd, &dwProcessId);
46
47 // check if the process Id is the one we're looking for
48 if (dwProcessId != pPM->dwProcessId)
49 {
50 return TRUE;
51 }
52
53 WcaLog(LOGMSG_VERBOSE, "Sending message to process id 0x%x", dwProcessId);
54
55 if (::SendMessageTimeoutW(hwnd, pPM->dwMessageId, 0, fQueryEndSession ? ENDSESSION_CLOSEAPP : 0, SMTO_BLOCK, pPM->dwTimeout, &dwResult))
56 {
57 WcaLog(LOGMSG_VERBOSE, "Result 0x%x", dwResult);
58
59 if (fQueryEndSession)
60 {
61 // If application said it was okay to close, do that.
62 if (dwResult)
63 {
64 ::SendMessageTimeoutW(hwnd, WM_ENDSESSION, TRUE, ENDSESSION_CLOSEAPP, SMTO_BLOCK, pPM->dwTimeout, &dwResult);
65 }
66 else // application said don't try to close it, so don't bother sending messages to any other top-level windows.
67 {
68 fContinueWindowsInProcess = FALSE;
69 }
70 }
71 }
72 else // log result message.
73 {
74 WcaLog(LOGMSG_VERBOSE, "Failed to send message id: %u, error: 0x%x", pPM->dwMessageId, ::GetLastError());
75 }
76
77 // so we know we succeeded
78 ::SetLastError(ERROR_SUCCESS);
79
80 return fContinueWindowsInProcess;
81}
82
83/******************************************************************
84 PromptToContinue - displays the prompt if the application is still
85 running.
86
87******************************************************************/
88static HRESULT PromptToContinue(
89 __in_z LPCWSTR wzApplication,
90 __in_z LPCWSTR wzPrompt
91 )
92{
93 HRESULT hr = S_OK;
94 UINT er = ERROR_SUCCESS;
95 PMSIHANDLE hRecMessage = NULL;
96 DWORD *prgProcessIds = NULL;
97 DWORD cProcessIds = 0;
98
99 hRecMessage = ::MsiCreateRecord(1);
100 ExitOnNull(hRecMessage, hr, E_OUTOFMEMORY, "Failed to create record for prompt.");
101
102 er = ::MsiRecordSetStringW(hRecMessage, 0, wzPrompt);
103 ExitOnWin32Error(er, hr, "Failed to set prompt record field string");
104
105 do
106 {
107 hr = ProcFindAllIdsFromExeName(wzApplication, &prgProcessIds, &cProcessIds);
108 if (SUCCEEDED(hr) && 0 < cProcessIds)
109 {
110 er = WcaProcessMessage(static_cast<INSTALLMESSAGE>(INSTALLMESSAGE_WARNING | MB_ABORTRETRYIGNORE | MB_DEFBUTTON3 | MB_ICONWARNING), hRecMessage);
111 if (IDABORT == er)
112 {
113 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
114 }
115 else if (IDRETRY == er)
116 {
117 hr = S_FALSE;
118 }
119 else if (IDIGNORE == er)
120 {
121 hr = S_OK;
122 }
123 else
124 {
125 ExitOnWin32Error(er, hr, "Unexpected return value from prompt to continue.");
126 }
127 }
128
129 ReleaseNullMem(prgProcessIds);
130 cProcessIds = 0;
131 } while (S_FALSE == hr);
132
133LExit:
134 ReleaseMem(prgProcessIds);
135 return hr;
136}
137
138/******************************************************************
139 SendProcessMessage - helper function to enumerate the top-level
140 windows and send to all matching a process ID.
141
142******************************************************************/
143void SendProcessMessage(
144 __in DWORD dwProcessId,
145 __in DWORD dwMessageId,
146 __in DWORD dwTimeout
147 )
148{
149 WcaLog(LOGMSG_VERBOSE, "Attempting to send process id 0x%x message id: %u", dwProcessId, dwMessageId);
150
151 PROCESS_AND_MESSAGE pm = { };
152 pm.dwProcessId = dwProcessId;
153 pm.dwMessageId = dwMessageId;
154 pm.dwTimeout = dwTimeout;
155
156 if (!::EnumWindows(EnumWindowsProc, reinterpret_cast<LPARAM>(&pm)))
157 {
158 DWORD dwLastError = ::GetLastError();
159 if (ERROR_SUCCESS != dwLastError)
160 {
161 WcaLog(LOGMSG_VERBOSE, "CloseApp enumeration error: 0x%x", dwLastError);
162 }
163 }
164}
165
166/******************************************************************
167 SendApplicationMessage - helper function to iterate through the
168 processes for the specified application and send all
169 applicable process Ids a message and give them time to process
170 the message.
171
172******************************************************************/
173void SendApplicationMessage(
174 __in LPCWSTR wzApplication,
175 __in DWORD dwMessageId,
176 __in DWORD dwTimeout
177 )
178{
179 DWORD *prgProcessIds = NULL;
180 DWORD cProcessIds = 0, iProcessId;
181 HRESULT hr = S_OK;
182
183 WcaLog(LOGMSG_VERBOSE, "Checking App: %ls ", wzApplication);
184
185 hr = ProcFindAllIdsFromExeName(wzApplication, &prgProcessIds, &cProcessIds);
186
187 if (SUCCEEDED(hr) && 0 < cProcessIds)
188 {
189 WcaLog(LOGMSG_VERBOSE, "App: %ls found running, %d processes, attempting to send message.", wzApplication, cProcessIds);
190
191 for (iProcessId = 0; iProcessId < cProcessIds; ++iProcessId)
192 {
193 SendProcessMessage(prgProcessIds[iProcessId], dwMessageId, dwTimeout);
194 }
195
196 ProcWaitForIds(prgProcessIds, cProcessIds, dwTimeout);
197 }
198
199 ReleaseMem(prgProcessIds);
200}
201
202/******************************************************************
203 SetRunningProcessProperty - helper function that sets the specified
204 property if there are any instances of the specified executable
205 running. Useful to show custom UI to ask for shutdown.
206******************************************************************/
207void SetRunningProcessProperty(
208 __in LPCWSTR wzApplication,
209 __in LPCWSTR wzProperty
210 )
211{
212 DWORD *prgProcessIds = NULL;
213 DWORD cProcessIds = 0;
214 HRESULT hr = S_OK;
215
216 WcaLog(LOGMSG_VERBOSE, "Checking App: %ls ", wzApplication);
217
218 hr = ProcFindAllIdsFromExeName(wzApplication, &prgProcessIds, &cProcessIds);
219
220 if (SUCCEEDED(hr) && 0 < cProcessIds)
221 {
222 WcaLog(LOGMSG_VERBOSE, "App: %ls found running, %d processes, setting '%ls' property.", wzApplication, cProcessIds, wzProperty);
223 WcaSetIntProperty(wzProperty, cProcessIds);
224 }
225
226 ReleaseMem(prgProcessIds);
227}
228
229/******************************************************************
230 TerminateProcesses - helper function that kills the provided set of
231 process ids such that they return a particular exit code.
232******************************************************************/
233void TerminateProcesses(
234 __in_ecount(cProcessIds) DWORD rgdwProcessIds[],
235 __in DWORD cProcessIds,
236 __in DWORD dwExitCode
237 )
238{
239 for (DWORD i = 0; i < cProcessIds; ++i)
240 {
241 HANDLE hProcess = ::OpenProcess(PROCESS_TERMINATE, FALSE, rgdwProcessIds[i]);
242 if (hProcess)
243 {
244 ::TerminateProcess(hProcess, dwExitCode);
245 ::CloseHandle(hProcess);
246 }
247 }
248}
249
250/******************************************************************
251 WixCloseApplications - entry point for WixCloseApplications Custom Action
252
253 called as Type 1 CustomAction (binary DLL) from Windows Installer
254 in InstallExecuteSequence before InstallFiles
255******************************************************************/
256extern "C" UINT __stdcall WixCloseApplications(
257 __in MSIHANDLE hInstall
258 )
259{
260 //AssertSz(FALSE, "debug WixCloseApplications");
261 HRESULT hr = S_OK;
262 UINT er = ERROR_SUCCESS;
263
264 LPWSTR pwzData = NULL;
265 LPWSTR pwzId = NULL;
266 LPWSTR pwzTarget = NULL;
267 LPWSTR pwzDescription = NULL;
268 LPWSTR pwzCondition = NULL;
269 LPWSTR pwzProperty = NULL;
270 DWORD dwAttributes = 0;
271 DWORD dwTimeout = 0;
272 DWORD dwTerminateExitCode = 0;
273 MSICONDITION condition = MSICONDITION_NONE;
274
275 DWORD cCloseApps = 0;
276
277 PMSIHANDLE hView = NULL;
278 PMSIHANDLE hRec = NULL;
279 MSIHANDLE hListboxTable = NULL;
280 MSIHANDLE hListboxColumns = NULL;
281
282 LPWSTR pwzCustomActionData = NULL;
283 //DWORD cchCustomActionData = 0;
284
285 //
286 // initialize
287 //
288 hr = WcaInitialize(hInstall, "WixCloseApplications");
289 ExitOnFailure(hr, "failed to initialize");
290
291 //
292 // loop through all the objects to be secured
293 //
294 hr = WcaOpenExecuteView(wzQUERY_CLOSEAPPS, &hView);
295 ExitOnFailure(hr, "failed to open view on Wix4CloseApplication table");
296 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
297 {
298 hr = WcaGetRecordString(hRec, QCA_ID, &pwzId);
299 ExitOnFailure(hr, "failed to get id from Wix4CloseApplication table");
300
301 hr = WcaGetRecordString(hRec, QCA_CONDITION, &pwzCondition);
302 ExitOnFailure(hr, "failed to get condition from Wix4CloseApplication table");
303
304 if (pwzCondition && *pwzCondition)
305 {
306 condition = ::MsiEvaluateConditionW(hInstall, pwzCondition);
307 if (MSICONDITION_ERROR == condition)
308 {
309 hr = E_INVALIDARG;
310 ExitOnFailure(hr, "failed to process condition for Wix4CloseApplication '%ls'", pwzId);
311 }
312 else if (MSICONDITION_FALSE == condition)
313 {
314 continue; // skip processing this target
315 }
316 }
317
318 hr = WcaGetRecordFormattedString(hRec, QCA_TARGET, &pwzTarget);
319 ExitOnFailure(hr, "failed to get target from Wix4CloseApplication table");
320
321 hr = WcaGetRecordFormattedString(hRec, QCA_DESCRIPTION, &pwzDescription);
322 ExitOnFailure(hr, "failed to get description from Wix4CloseApplication table");
323
324 hr = WcaGetRecordInteger(hRec, QCA_ATTRIBUTES, reinterpret_cast<int*>(&dwAttributes));
325 ExitOnFailure(hr, "failed to get attributes from Wix4CloseApplication table");
326
327 hr = WcaGetRecordFormattedString(hRec, QCA_PROPERTY, &pwzProperty);
328 ExitOnFailure(hr, "failed to get property from Wix4CloseApplication table");
329
330 hr = WcaGetRecordInteger(hRec, QCA_TERMINATEEXITCODE, reinterpret_cast<int*>(&dwTerminateExitCode));
331 if (S_FALSE == hr)
332 {
333 dwTerminateExitCode = 0;
334 hr = S_OK;
335 }
336 ExitOnFailure(hr, "failed to get terminate exit-code from Wix4CloseApplication table");
337
338 hr = WcaGetRecordInteger(hRec, QCA_TIMEOUT, reinterpret_cast<int*>(&dwTimeout));
339 if (S_FALSE == hr)
340 {
341 dwTimeout = DEFAULT_PROCESS_EXIT_WAIT_TIME;
342 hr = S_OK;
343 }
344 ExitOnFailure(hr, "failed to get timeout from Wix4CloseApplication table");
345
346 // Before trying any changes to the machine, prompt if requested.
347 if (dwAttributes & CLOSEAPP_ATTRIBUTE_PROMPTTOCONTINUE)
348 {
349 hr = PromptToContinue(pwzTarget, pwzDescription ? pwzDescription : L"");
350 if (HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) == hr)
351 {
352 // Skip error message if user canceled.
353 ExitFunction();
354 }
355 ExitOnFailure(hr, "Failure while prompting user to continue to close application.");
356 }
357
358 //
359 // send WM_CLOSE or WM_QUERYENDSESSION to currently running applications
360 //
361 if (dwAttributes & CLOSEAPP_ATTRIBUTE_CLOSEMESSAGE)
362 {
363 SendApplicationMessage(pwzTarget, WM_CLOSE, dwTimeout);
364 }
365
366 if (dwAttributes & CLOSEAPP_ATTRIBUTE_ENDSESSIONMESSAGE)
367 {
368 SendApplicationMessage(pwzTarget, WM_QUERYENDSESSION, dwTimeout);
369 }
370
371 //
372 // Pass the targets to the deferred action in case the app comes back
373 // even if we close it now.
374 //
375 if (dwAttributes & (CLOSEAPP_ATTRIBUTE_ELEVATEDCLOSEMESSAGE | CLOSEAPP_ATTRIBUTE_ELEVATEDENDSESSIONMESSAGE | CLOSEAPP_ATTRIBUTE_REBOOTPROMPT | CLOSEAPP_ATTRIBUTE_TERMINATEPROCESS))
376 {
377 hr = WcaWriteStringToCaData(pwzTarget, &pwzCustomActionData);
378 ExitOnFailure(hr, "failed to add target data to CustomActionData");
379
380 hr = WcaWriteIntegerToCaData(dwAttributes, &pwzCustomActionData);
381 ExitOnFailure(hr, "failed to add attribute data to CustomActionData");
382
383 hr = WcaWriteIntegerToCaData(dwTimeout, &pwzCustomActionData);
384 ExitOnFailure(hr, "failed to add timeout data to CustomActionData");
385
386 hr = WcaWriteIntegerToCaData(dwTerminateExitCode, &pwzCustomActionData);
387 ExitOnFailure(hr, "failed to add timeout data to CustomActionData");
388 }
389
390 if (pwzProperty && *pwzProperty)
391 {
392 SetRunningProcessProperty(pwzTarget, pwzProperty);
393 }
394
395 ++cCloseApps;
396 }
397
398 // if we looped through all records all is well
399 if (E_NOMOREITEMS == hr)
400 {
401 hr = S_OK;
402 }
403 ExitOnFailure(hr, "failed while looping through all apps to close");
404
405 //
406 // Do the UI dance now.
407 //
408 /*
409
410 TODO: Do this eventually
411
412 if (cCloseApps)
413 {
414 while (TRUE)
415 {
416 for (DWORD i = 0; i < cCloseApps; ++i)
417 {
418 hr = WcaAddTempRecord(&hListboxTable, &hListboxColumns, L"ListBox", NULL, 0, 4, L"FileInUseProcess", i, target, description);
419 if (FAILED(hr))
420 {
421 }
422 }
423 }
424 }
425 */
426
427 //
428 // schedule the custom action and add to progress bar
429 //
430 if (pwzCustomActionData && *pwzCustomActionData)
431 {
432 Assert(0 < cCloseApps);
433
434 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"CloseApplicationsDeferred"), pwzCustomActionData, cCloseApps * COST_CLOSEAPP);
435 ExitOnFailure(hr, "failed to schedule CloseApplicationsDeferred action");
436 }
437
438LExit:
439 if (hListboxColumns)
440 {
441 ::MsiCloseHandle(hListboxColumns);
442 }
443 if (hListboxTable)
444 {
445 ::MsiCloseHandle(hListboxTable);
446 }
447
448 ReleaseStr(pwzCustomActionData);
449 ReleaseStr(pwzData);
450 ReleaseStr(pwzProperty);
451 ReleaseStr(pwzCondition);
452 ReleaseStr(pwzDescription);
453 ReleaseStr(pwzTarget);
454 ReleaseStr(pwzId);
455
456 if (FAILED(hr))
457 {
458 er = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) == hr ? ERROR_INSTALL_USEREXIT : ERROR_INSTALL_FAILURE;
459 }
460 return WcaFinalize(er);
461}
462
463
464/******************************************************************
465 WixCloseApplicationsDeferred - entry point for
466 WixCloseApplicationsDeferred Custom Action
467 called as Type 1025 CustomAction
468 (deferred binary DLL)
469
470 NOTE: deferred CustomAction since it modifies the machine
471 NOTE: CustomActionData == wzTarget\tdwAttributes\tdwTimeout\tdwTerminateExitCode\t...
472******************************************************************/
473extern "C" UINT __stdcall WixCloseApplicationsDeferred(
474 __in MSIHANDLE hInstall
475 )
476{
477 //AssertSz(FALSE, "debug WixCloseApplicationsDeferred");
478 HRESULT hr = S_OK;
479 DWORD er = ERROR_SUCCESS;
480
481 LPWSTR pwz = NULL;
482 LPWSTR pwzData = NULL;
483 LPWSTR pwzTarget = NULL;
484 DWORD dwAttributes = 0;
485 DWORD dwTimeout = 0;
486 DWORD dwTerminateExitCode = 0;
487
488 DWORD *prgProcessIds = NULL;
489 DWORD cProcessIds = 0;
490
491 //
492 // initialize
493 //
494 hr = WcaInitialize(hInstall, "WixCloseApplicationsDeferred");
495 ExitOnFailure(hr, "failed to initialize");
496
497 hr = WcaGetProperty(L"CustomActionData", &pwzData);
498 ExitOnFailure(hr, "failed to get CustomActionData");
499
500 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData);
501
502 pwz = pwzData;
503
504 //
505 // loop through all the passed in data
506 //
507 while (pwz && *pwz)
508 {
509 hr = WcaReadStringFromCaData(&pwz, &pwzTarget);
510 ExitOnFailure(hr, "failed to process target from CustomActionData");
511
512 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&dwAttributes));
513 ExitOnFailure(hr, "failed to process attributes from CustomActionData");
514
515 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&dwTimeout));
516 ExitOnFailure(hr, "failed to process timeout from CustomActionData");
517
518 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&dwTerminateExitCode));
519 ExitOnFailure(hr, "failed to process terminate exit code from CustomActionData");
520
521 WcaLog(LOGMSG_VERBOSE, "Checking for App: %ls Attributes: %d", pwzTarget, dwAttributes);
522
523 //
524 // send WM_CLOSE or WM_QUERYENDSESSION to currently running applications
525 //
526 if (dwAttributes & CLOSEAPP_ATTRIBUTE_ELEVATEDCLOSEMESSAGE)
527 {
528 SendApplicationMessage(pwzTarget, WM_CLOSE, dwTimeout);
529 }
530
531 if (dwAttributes & CLOSEAPP_ATTRIBUTE_ELEVATEDENDSESSIONMESSAGE)
532 {
533 SendApplicationMessage(pwzTarget, WM_QUERYENDSESSION, dwTimeout);
534 }
535
536 // If we find that an app that we need closed is still runing, require a
537 // restart or kill the process as directed.
538 ProcFindAllIdsFromExeName(pwzTarget, &prgProcessIds, &cProcessIds);
539 if (0 < cProcessIds)
540 {
541 if (dwAttributes & CLOSEAPP_ATTRIBUTE_REBOOTPROMPT)
542 {
543 WcaLog(LOGMSG_VERBOSE, "App: %ls found running, requiring a reboot.", pwzTarget);
544
545 WcaDeferredActionRequiresReboot();
546 }
547 else if (dwAttributes & CLOSEAPP_ATTRIBUTE_TERMINATEPROCESS)
548 {
549 TerminateProcesses(prgProcessIds, cProcessIds, dwTerminateExitCode);
550 }
551 }
552
553 hr = WcaProgressMessage(COST_CLOSEAPP, FALSE);
554 ExitOnFailure(hr, "failed to send progress message");
555 }
556
557LExit:
558 ReleaseMem(prgProcessIds);
559
560 ReleaseStr(pwzTarget);
561 ReleaseStr(pwzData);
562
563 if (FAILED(hr))
564 {
565 er = ERROR_INSTALL_FAILURE;
566 }
567 return WcaFinalize(er);
568}
diff --git a/src/ext/Util/ca/CustomMsiErrors.h b/src/ext/Util/ca/CustomMsiErrors.h
new file mode 100644
index 00000000..3218b61b
--- /dev/null
+++ b/src/ext/Util/ca/CustomMsiErrors.h
@@ -0,0 +1,32 @@
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 msierrSecureObjectsFailedCreateSD 25520
6#define msierrSecureObjectsFailedSet 25521
7#define msierrSecureObjectsUnknownType 25522
8
9#define msierrXmlFileFailedRead 25530
10#define msierrXmlFileFailedOpen 25531
11#define msierrXmlFileFailedSelect 25532
12#define msierrXmlFileFailedSave 25533
13
14#define msierrXmlConfigFailedRead 25540
15#define msierrXmlConfigFailedOpen 25541
16#define msierrXmlConfigFailedSelect 25542
17#define msierrXmlConfigFailedSave 25543
18
19#define msierrPERFMONFailedRegisterDLL 26251
20#define msierrPERFMONFailedUnregisterDLL 26252
21#define msierrInstallPerfCounterData 26253
22#define msierrUninstallPerfCounterData 26254
23
24#define msierrSMBFailedCreate 26301
25#define msierrSMBFailedDrop 26302
26#define msierrUSRFailedUserCreate 26401
27#define msierrUSRFailedUserCreatePswd 26402
28#define msierrUSRFailedUserGroupAdd 26403
29#define msierrUSRFailedUserCreateExists 26404
30#define msierrUSRFailedGrantLogonAsService 26405
31
32//Last available is 26450 \ No newline at end of file
diff --git a/src/ext/Util/ca/FormatFiles.cpp b/src/ext/Util/ca/FormatFiles.cpp
new file mode 100644
index 00000000..d1533999
--- /dev/null
+++ b/src/ext/Util/ca/FormatFiles.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
5const UINT COST_FILEFORMATTING = 2000;
6
7
8//
9// WixSchedFormatFiles - immediate CA to schedule format files CAs
10//
11extern "C" UINT __stdcall WixSchedFormatFiles(
12 __in MSIHANDLE hInstall
13 )
14{
15 HRESULT hr = S_OK;
16 UINT er = ERROR_SUCCESS;
17 PSCZ sczBinaryKey;
18 PSCZ sczFileKey;
19 PSCZ sczComponentKey;
20 PSCZ sczFormattedFile;
21 PSCZ sczFilePath;
22 PMSIHANDLE hView;
23 PMSIHANDLE hRec;
24 PSCZ sczFileContent;
25 PSCZ sczFormattedContent;
26 PSCZ sczExecCustomActionData;
27 PSCZ sczRollbackCustomActionData;
28
29 LPCWSTR wzQuery =
30 L"SELECT `Wix4FormatFile`.`Binary_`, `Wix4FormatFile`.`File_`, `File`.`Component_` "
31 L"FROM `Wix4FormatFile`, `File` "
32 L"WHERE `Wix4FormatFile`.`File_` = `File`.`File`";
33 enum eQuery { eqBinaryKey = 1, eqFileKey, eqComponentKey };
34
35 // initialize
36 hr = WcaInitialize(hInstall, "WixSchedFormatFiles");
37 ExitOnFailure(hr, "Failed to initialize for WixSchedFormatFiles.");
38
39 // query and loop through all the files
40 hr = WcaOpenExecuteView(wzQuery, &hView);
41 ExitOnFailure(hr, "Failed to open view on Wix4FormatFile table");
42
43 DWORD cFiles = 0;
44 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
45 {
46 ++cFiles;
47
48 hr = WcaGetRecordString(hRec, eqBinaryKey, &sczBinaryKey);
49 ExitOnFailure(hr, "Failed to get Binary table key.");
50
51 hr = WcaGetRecordString(hRec, eqFileKey, &sczFileKey);
52 ExitOnFailure(hr, "Failed to get File table key.");
53
54 hr = WcaGetRecordString(hRec, eqComponentKey, &sczComponentKey);
55 ExitOnFailure(hr, "Failed to get Component table key.");
56
57 // we need to know if the component's being installed, uninstalled, or reinstalled
58 WCA_TODO todo = WcaGetComponentToDo(sczComponentKey);
59 if (WCA_TODO_INSTALL == todo || WCA_TODO_REINSTALL == todo)
60 {
61 // turn the file key into the path to the target file
62 hr = StrAllocFormatted(&sczFormattedFile, L"[#%ls]", sczFileKey);
63 ExitOnFailure(hr, "Failed to format file string for file: %ls", sczFileKey);
64 hr = WcaGetFormattedString(sczFormattedFile, &sczFilePath);
65 ExitOnFailure(hr, "Failed to get path for file: %ls", sczFileKey);
66
67 // extract binary to string
68 WCA_ENCODING encoding = WCA_ENCODING_UNKNOWN;
69 hr = WcaExtractBinaryToString(sczBinaryKey, &sczFileContent, &encoding);
70 ExitOnFailure(hr, "Failed to extract binary: %ls", sczBinaryKey);
71
72 // format string
73 hr = WcaGetFormattedString(sczFileContent, &sczFormattedContent);
74 ExitOnFailure(hr, "Failed to format file content: %ls", sczFileContent);
75
76 // write to deferred custom action data
77 hr = WcaWriteStringToCaData(sczFilePath, &sczExecCustomActionData);
78 ExitOnFailure(hr, "Failed to write deferred custom action data for file: %ls", sczFilePath);
79
80 hr = WcaWriteIntegerToCaData(encoding, &sczExecCustomActionData);
81 ExitOnFailure(hr, "Failed to write deferred custom action data for encoding: %d", encoding);
82
83 hr = WcaWriteStringToCaData(sczFormattedContent, &sczExecCustomActionData);
84 ExitOnFailure(hr, "Failed to write deferred custom action data for file content: %ls", sczFilePath);
85
86 // write to rollback custom action data
87 hr = WcaWriteStringToCaData(sczFilePath, &sczRollbackCustomActionData);
88 ExitOnFailure(hr, "Failed to write rollback custom action data for file: %ls", sczFilePath);
89
90 hr = WcaWriteIntegerToCaData(encoding, &sczRollbackCustomActionData);
91 ExitOnFailure(hr, "Failed to write deferred custom action data for encoding: %d", encoding);
92
93 hr = WcaWriteStringToCaData(sczFileContent, &sczRollbackCustomActionData);
94 ExitOnFailure(hr, "Failed to write rollback custom action data for file content: %ls", sczFilePath);
95 }
96 }
97
98 // reaching the end of the list is actually a good thing, not an error
99 if (E_NOMOREITEMS == hr)
100 {
101 hr = S_OK;
102 }
103 ExitOnFailure(hr, "Failure occurred while processing Wix4FormatFile table");
104
105 // schedule deferred CAs if there's anything to do
106 if (sczRollbackCustomActionData && *sczRollbackCustomActionData)
107 {
108 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"RollbackFormatFiles"), sczRollbackCustomActionData, cFiles * COST_FILEFORMATTING);
109 ExitOnFailure(hr, "Failed to schedule RollbackFormatFiles");
110 }
111
112 if (sczExecCustomActionData && *sczExecCustomActionData)
113 {
114 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"ExecFormatFiles"), sczExecCustomActionData, cFiles * COST_FILEFORMATTING);
115 ExitOnFailure(hr, "Failed to schedule ExecFormatFiles");
116 }
117
118LExit:
119 return WcaFinalize(er = FAILED(hr) ? ERROR_INSTALL_FAILURE : er);
120}
121
122
123//
124// WixExecFormatFiles - deferred and rollback CAs to write formatted files
125//
126extern "C" UINT __stdcall WixExecFormatFiles(
127 __in MSIHANDLE hInstall
128 )
129{
130 HRESULT hr = S_OK;
131 UINT er = ERROR_SUCCESS;
132 PSCZ sczCustomActionData;
133 LPWSTR pwz = NULL;
134 PSCZ sczFilePath;
135 PSCZ sczFileContent;
136 LPSTR psz = NULL;
137
138 // initialize
139 hr = WcaInitialize(hInstall, "WixExecFormatFiles");
140 ExitOnFailure(hr, "Failed to initialize for WixExecFormatFiles.");
141
142 hr = WcaGetProperty(L"CustomActionData", &sczCustomActionData);
143 ExitOnFailure(hr, "Failed to get CustomActionData.");
144#ifdef _DEBUG
145 WcaLog(LOGMSG_STANDARD, "CustomActionData: %ls", sczCustomActionData);
146#endif
147
148 // loop through all the passed in data
149 pwz = sczCustomActionData;
150 while (pwz && *pwz)
151 {
152 // extract the custom action data
153 hr = WcaReadStringFromCaData(&pwz, &sczFilePath);
154 ExitOnFailure(hr, "Failed to read file path from custom action data");
155
156 WCA_ENCODING encoding = WCA_ENCODING_UNKNOWN;
157 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&encoding));
158 ExitOnFailure(hr, "Failed to read encoding from custom action data");
159
160 hr = WcaReadStringFromCaData(&pwz, &sczFileContent);
161 ExitOnFailure(hr, "Failed to read file content from custom action data");
162
163 // re-encode content
164 LPCBYTE pbData = NULL;
165 size_t cbData = 0;
166 switch (encoding)
167 {
168 case WCA_ENCODING_UTF_16:
169 pbData = reinterpret_cast<LPCBYTE>(LPCWSTR(sczFileContent));
170 cbData = lstrlenW(sczFileContent) * sizeof(WCHAR);
171 break;
172
173 case WCA_ENCODING_UTF_8:
174 hr = StrAnsiAllocString(&psz, sczFileContent, 0, CP_UTF8);
175 ExitOnFailure(hr, "Failed to convert Unicode to UTF-8.");
176 pbData = reinterpret_cast<LPCBYTE>(psz);
177
178 hr = ::StringCbLengthA(psz, STRSAFE_MAX_CCH, &cbData);
179 ExitOnFailure(hr, "Failed to count UTF-8 bytes.");
180 break;
181
182 case WCA_ENCODING_ANSI:
183 hr = StrAnsiAllocString(&psz, sczFileContent, 0, CP_ACP);
184 ExitOnFailure(hr, "Failed to convert Unicode to ANSI.");
185 pbData = reinterpret_cast<LPCBYTE>(psz);
186
187 hr = ::StringCbLengthA(psz, STRSAFE_MAX_CCH, &cbData);
188 ExitOnFailure(hr, "Failed to count UTF-8 bytes.");
189 break;
190
191 default:
192 break;
193 }
194
195#ifdef _DEBUG
196 WcaLog(LOGMSG_STANDARD, "File: %ls", sczCustomActionData);
197 WcaLog(LOGMSG_STANDARD, "Content: %ls", sczFileContent);
198#endif
199
200 // write file and preserve modified time
201 FILETIME filetime;
202
203 hr = FileGetTime(sczFilePath, NULL, NULL, &filetime);
204 ExitOnFailure(hr, "Failed to get modified time of file : %ls", sczFilePath);
205
206 hr = FileWrite(sczFilePath, FILE_ATTRIBUTE_NORMAL, pbData, cbData, NULL);
207 ExitOnFailure(hr, "Failed to write file content: %ls", sczFilePath);
208
209 hr = FileSetTime(sczFilePath, NULL, NULL, &filetime);
210 ExitOnFailure(hr, "Failed to set modified time of file : %ls", sczFilePath);
211
212 // Tick the progress bar
213 hr = WcaProgressMessage(COST_FILEFORMATTING, FALSE);
214 ExitOnFailure(hr, "Failed to tick progress bar for file: %ls", sczFilePath);
215 }
216
217LExit:
218 ReleaseStr(psz);
219
220 return WcaFinalize(er = FAILED(hr) ? ERROR_INSTALL_FAILURE : er);
221}
diff --git a/src/ext/Util/ca/OsInfo.cpp b/src/ext/Util/ca/OsInfo.cpp
new file mode 100644
index 00000000..4783673e
--- /dev/null
+++ b/src/ext/Util/ca/OsInfo.cpp
@@ -0,0 +1,487 @@
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 we'll pick up from later SDKs
6#define SM_TABLETPC 86
7#define SM_MEDIACENTER 87
8#define SM_STARTER 88
9#define SM_SERVERR2 89
10#define VER_SUITE_WH_SERVER 0x00008000
11
12/********************************************************************
13WixQueryOsInfo - entry point for WixQueryOsInfo custom action
14
15 Called as Type 1 custom action (DLL from the Binary table) from
16 Windows Installer to set properties that identify OS information
17 and predefined directories
18********************************************************************/
19extern "C" UINT __stdcall WixQueryOsInfo(
20 __in MSIHANDLE hInstall
21 )
22{
23 HRESULT hr = S_OK;
24 DWORD er = ERROR_SUCCESS;
25 OSVERSIONINFOEXW ovix = {0};
26
27 hr = WcaInitialize(hInstall, "WixQueryOsInfo");
28 ExitOnFailure(hr, "WixQueryOsInfo failed to initialize");
29
30 // identify product suites
31 ovix.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
32 #pragma warning(suppress: 4996) //TODO: use osutil
33 ::GetVersionExW(reinterpret_cast<LPOSVERSIONINFOW>(&ovix));
34
35 if (VER_SUITE_SMALLBUSINESS == (ovix.wSuiteMask & VER_SUITE_SMALLBUSINESS))
36 {
37 WcaSetIntProperty(L"WIX_SUITE_SMALLBUSINESS", 1);
38 }
39
40 if (VER_SUITE_ENTERPRISE == (ovix.wSuiteMask & VER_SUITE_ENTERPRISE))
41 {
42 WcaSetIntProperty(L"WIX_SUITE_ENTERPRISE", 1);
43 }
44
45 if (VER_SUITE_BACKOFFICE == (ovix.wSuiteMask & VER_SUITE_BACKOFFICE))
46 {
47 WcaSetIntProperty(L"WIX_SUITE_BACKOFFICE", 1);
48 }
49
50 if (VER_SUITE_COMMUNICATIONS == (ovix.wSuiteMask & VER_SUITE_COMMUNICATIONS))
51 {
52 WcaSetIntProperty(L"WIX_SUITE_COMMUNICATIONS", 1);
53 }
54
55 if (VER_SUITE_TERMINAL == (ovix.wSuiteMask & VER_SUITE_TERMINAL))
56 {
57 WcaSetIntProperty(L"WIX_SUITE_TERMINAL", 1);
58 }
59
60 if (VER_SUITE_SMALLBUSINESS_RESTRICTED == (ovix.wSuiteMask & VER_SUITE_SMALLBUSINESS_RESTRICTED))
61 {
62 WcaSetIntProperty(L"WIX_SUITE_SMALLBUSINESS_RESTRICTED", 1);
63 }
64
65 if (VER_SUITE_EMBEDDEDNT == (ovix.wSuiteMask & VER_SUITE_EMBEDDEDNT))
66 {
67 WcaSetIntProperty(L"WIX_SUITE_EMBEDDEDNT", 1);
68 }
69
70 if (VER_SUITE_DATACENTER == (ovix.wSuiteMask & VER_SUITE_DATACENTER))
71 {
72 WcaSetIntProperty(L"WIX_SUITE_DATACENTER", 1);
73 }
74
75 if (VER_SUITE_SINGLEUSERTS == (ovix.wSuiteMask & VER_SUITE_SINGLEUSERTS))
76 {
77 WcaSetIntProperty(L"WIX_SUITE_SINGLEUSERTS", 1);
78 }
79
80 if (VER_SUITE_PERSONAL == (ovix.wSuiteMask & VER_SUITE_PERSONAL))
81 {
82 WcaSetIntProperty(L"WIX_SUITE_PERSONAL", 1);
83 }
84
85 if (VER_SUITE_BLADE == (ovix.wSuiteMask & VER_SUITE_BLADE))
86 {
87 WcaSetIntProperty(L"WIX_SUITE_BLADE", 1);
88 }
89
90 if (VER_SUITE_EMBEDDED_RESTRICTED == (ovix.wSuiteMask & VER_SUITE_EMBEDDED_RESTRICTED))
91 {
92 WcaSetIntProperty(L"WIX_SUITE_EMBEDDED_RESTRICTED", 1);
93 }
94
95 if (VER_SUITE_SECURITY_APPLIANCE == (ovix.wSuiteMask & VER_SUITE_SECURITY_APPLIANCE))
96 {
97 WcaSetIntProperty(L"WIX_SUITE_SECURITY_APPLIANCE", 1);
98 }
99
100 if (VER_SUITE_STORAGE_SERVER == (ovix.wSuiteMask & VER_SUITE_STORAGE_SERVER))
101 {
102 WcaSetIntProperty(L"WIX_SUITE_STORAGE_SERVER", 1);
103 }
104
105 if (VER_SUITE_COMPUTE_SERVER == (ovix.wSuiteMask & VER_SUITE_COMPUTE_SERVER))
106 {
107 WcaSetIntProperty(L"WIX_SUITE_COMPUTE_SERVER", 1);
108 }
109
110 if (VER_SUITE_WH_SERVER == (ovix.wSuiteMask & VER_SUITE_WH_SERVER))
111 {
112 WcaSetIntProperty(L"WIX_SUITE_WH_SERVER", 1);
113 }
114
115 // only for XP and later
116 if (5 < ovix.dwMajorVersion || (5 == ovix.dwMajorVersion && 0 < ovix.dwMinorVersion))
117 {
118 if (::GetSystemMetrics(SM_SERVERR2))
119 {
120 WcaSetIntProperty(L"WIX_SUITE_SERVERR2", 1);
121 }
122
123 if (::GetSystemMetrics(SM_MEDIACENTER))
124 {
125 WcaSetIntProperty(L"WIX_SUITE_MEDIACENTER", 1);
126 }
127
128 if (::GetSystemMetrics(SM_STARTER))
129 {
130 WcaSetIntProperty(L"WIX_SUITE_STARTER", 1);
131 }
132
133 if (::GetSystemMetrics(SM_TABLETPC))
134 {
135 WcaSetIntProperty(L"WIX_SUITE_TABLETPC", 1);
136 }
137 }
138
139LExit:
140 if (FAILED(hr))
141 er = ERROR_INSTALL_FAILURE;
142 return WcaFinalize(er);
143}
144
145/********************************************************************
146WixQueryOsDirs - entry point for WixQueryOsDirs custom action
147
148 Called as Type 1 custom action (DLL from the Binary table) from
149 Windows Installer to set properties that identify predefined directories
150********************************************************************/
151extern "C" UINT __stdcall WixQueryOsDirs(
152 __in MSIHANDLE hInstall
153 )
154{
155 HRESULT hr = S_OK;
156 DWORD er = ERROR_SUCCESS;
157
158 hr = WcaInitialize(hInstall, "WixQueryOsDirs");
159 ExitOnFailure(hr, "WixQueryOsDirs failed to initialize");
160
161 // get the paths of the CSIDLs that represent real paths and for which MSI
162 // doesn't yet have standard folder properties
163 WCHAR path[MAX_PATH];
164 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_ADMINTOOLS, NULL, SHGFP_TYPE_CURRENT, path))
165 {
166 WcaSetProperty(L"WIX_DIR_ADMINTOOLS", path);
167 }
168
169 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_ALTSTARTUP, NULL, SHGFP_TYPE_CURRENT, path))
170 {
171 WcaSetProperty(L"WIX_DIR_ALTSTARTUP", path);
172 }
173
174 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_CDBURN_AREA, NULL, SHGFP_TYPE_CURRENT, path))
175 {
176 WcaSetProperty(L"WIX_DIR_CDBURN_AREA", path);
177 }
178
179 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_COMMON_ADMINTOOLS, NULL, SHGFP_TYPE_CURRENT, path))
180 {
181 WcaSetProperty(L"WIX_DIR_COMMON_ADMINTOOLS", path);
182 }
183
184 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_COMMON_ALTSTARTUP, NULL, SHGFP_TYPE_CURRENT, path))
185 {
186 WcaSetProperty(L"WIX_DIR_COMMON_ALTSTARTUP", path);
187 }
188
189 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_COMMON_DOCUMENTS, NULL, SHGFP_TYPE_CURRENT, path))
190 {
191 WcaSetProperty(L"WIX_DIR_COMMON_DOCUMENTS", path);
192 }
193
194 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_COMMON_FAVORITES, NULL, SHGFP_TYPE_CURRENT, path))
195 {
196 WcaSetProperty(L"WIX_DIR_COMMON_FAVORITES", path);
197 }
198
199 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_COMMON_MUSIC, NULL, SHGFP_TYPE_CURRENT, path))
200 {
201 WcaSetProperty(L"WIX_DIR_COMMON_MUSIC", path);
202 }
203
204 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_COMMON_PICTURES, NULL, SHGFP_TYPE_CURRENT, path))
205 {
206 WcaSetProperty(L"WIX_DIR_COMMON_PICTURES", path);
207 }
208
209 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_COMMON_VIDEO, NULL, SHGFP_TYPE_CURRENT, path))
210 {
211 WcaSetProperty(L"WIX_DIR_COMMON_VIDEO", path);
212 }
213
214 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_COOKIES, NULL, SHGFP_TYPE_CURRENT, path))
215 {
216 WcaSetProperty(L"WIX_DIR_COOKIES", path);
217 }
218
219 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_DESKTOP, NULL, SHGFP_TYPE_CURRENT, path))
220 {
221 WcaSetProperty(L"WIX_DIR_DESKTOP", path);
222 }
223
224 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_HISTORY, NULL, SHGFP_TYPE_CURRENT, path))
225 {
226 WcaSetProperty(L"WIX_DIR_HISTORY", path);
227 }
228
229 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_INTERNET_CACHE, NULL, SHGFP_TYPE_CURRENT, path))
230 {
231 WcaSetProperty(L"WIX_DIR_INTERNET_CACHE", path);
232 }
233
234 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_MYMUSIC, NULL, SHGFP_TYPE_CURRENT, path))
235 {
236 WcaSetProperty(L"WIX_DIR_MYMUSIC", path);
237 }
238
239 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_MYPICTURES, NULL, SHGFP_TYPE_CURRENT, path))
240 {
241 WcaSetProperty(L"WIX_DIR_MYPICTURES", path);
242 }
243
244 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_MYVIDEO, NULL, SHGFP_TYPE_CURRENT, path))
245 {
246 WcaSetProperty(L"WIX_DIR_MYVIDEO", path);
247 }
248
249 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_NETHOOD, NULL, SHGFP_TYPE_CURRENT, path))
250 {
251 WcaSetProperty(L"WIX_DIR_NETHOOD", path);
252 }
253
254 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, path))
255 {
256 WcaSetProperty(L"WIX_DIR_PERSONAL", path);
257 }
258
259 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_PRINTHOOD, NULL, SHGFP_TYPE_CURRENT, path))
260 {
261 WcaSetProperty(L"WIX_DIR_PRINTHOOD", path);
262 }
263
264 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_PROFILE, NULL, SHGFP_TYPE_CURRENT, path))
265 {
266 WcaSetProperty(L"WIX_DIR_PROFILE", path);
267 }
268
269 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_RECENT, NULL, SHGFP_TYPE_CURRENT, path))
270 {
271 WcaSetProperty(L"WIX_DIR_RECENT", path);
272 }
273
274 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_RESOURCES, NULL, SHGFP_TYPE_CURRENT, path))
275 {
276 WcaSetProperty(L"WIX_DIR_RESOURCES", path);
277 }
278
279LExit:
280 if (FAILED(hr))
281 er = ERROR_INSTALL_FAILURE;
282 return WcaFinalize(er);
283}
284
285
286/********************************************************************
287SetPropertyWellKnownSID
288
289 Set a property with the localized name of a well known windows SID
290********************************************************************/
291static HRESULT SetPropertyWellKnownSID(
292 __in WELL_KNOWN_SID_TYPE sidType,
293 __in LPCWSTR wzPropertyName,
294 __in BOOL fIncludeDomainName
295 )
296{
297 HRESULT hr = S_OK;
298 PSID psid = NULL;
299 WCHAR wzRefDomain[MAX_PATH] = {0};
300 SID_NAME_USE nameUse;
301 DWORD refSize = MAX_PATH;
302 WCHAR wzName[MAX_PATH] = {0};
303 LPWSTR pwzPropertyValue = NULL;
304 DWORD size = MAX_PATH;
305
306 hr = AclGetWellKnownSid(sidType, &psid);
307 ExitOnFailure(hr, "Failed to get SID; skipping account %ls", wzPropertyName);
308
309 if (!::LookupAccountSidW(NULL, psid, wzName, &size, wzRefDomain, &refSize, &nameUse))
310 {
311 ExitWithLastError(hr, "Failed to look up account for SID; skipping account %ls.", wzPropertyName);
312 }
313
314 if (fIncludeDomainName)
315 {
316 hr = StrAllocFormatted(&pwzPropertyValue, L"%s\\%s", wzRefDomain, wzName);
317 ExitOnFailure(hr, "Failed to format property value");
318
319 hr = WcaSetProperty(wzPropertyName, pwzPropertyValue);
320 ExitOnFailure(hr, "Failed write domain\\name property");
321 }
322 else
323 {
324 hr = WcaSetProperty(wzPropertyName, wzName);
325 ExitOnFailure(hr, "Failed write to name-only property");
326 }
327
328LExit:
329 if (NULL != psid)
330 {
331 ::LocalFree(psid);
332 }
333 ReleaseStr(pwzPropertyValue);
334 return hr;
335}
336
337/********************************************************************
338WixQueryOsWellKnownSID - entry point for WixQueryOsWellKnownSID custom action
339
340 Called as Type 1 custom action (DLL from the Binary table) from
341 Windows Installer to set properties with the localized names of built-in
342 Windows Security IDs
343********************************************************************/
344extern "C" UINT __stdcall WixQueryOsWellKnownSID(
345 __in MSIHANDLE hInstall
346 )
347{
348 HRESULT hr = S_OK;
349 DWORD er = ERROR_SUCCESS;
350
351 hr = WcaInitialize(hInstall, "WixQueryOsWellKnownSID");
352 ExitOnFailure(hr, "WixQueryOsWellKnownSID failed to initialize");
353
354 SetPropertyWellKnownSID(WinLocalSystemSid, L"WIX_ACCOUNT_LOCALSYSTEM", TRUE);
355 SetPropertyWellKnownSID(WinLocalServiceSid, L"WIX_ACCOUNT_LOCALSERVICE", TRUE);
356 SetPropertyWellKnownSID(WinNetworkServiceSid, L"WIX_ACCOUNT_NETWORKSERVICE", TRUE);
357 SetPropertyWellKnownSID(WinBuiltinAdministratorsSid, L"WIX_ACCOUNT_ADMINISTRATORS", TRUE);
358 SetPropertyWellKnownSID(WinBuiltinUsersSid, L"WIX_ACCOUNT_USERS", TRUE);
359 SetPropertyWellKnownSID(WinBuiltinGuestsSid, L"WIX_ACCOUNT_GUESTS", TRUE);
360 SetPropertyWellKnownSID(WinBuiltinPerfLoggingUsersSid, L"WIX_ACCOUNT_PERFLOGUSERS", TRUE);
361 SetPropertyWellKnownSID(WinBuiltinPerfLoggingUsersSid, L"WIX_ACCOUNT_PERFLOGUSERS_NODOMAIN", FALSE);
362
363LExit:
364 if (FAILED(hr))
365 {
366 er = ERROR_INSTALL_FAILURE;
367 }
368 return WcaFinalize(er);
369}
370
371
372/********************************************************************
373DetectWDDMDriver
374
375 Set a property if the driver on the machine is a WDDM driver. One
376 reliable way to detect the presence of a WDDM driver is to try and
377 use the Direct3DCreate9Ex() function. This method attempts that
378 then sets the property appropriately.
379********************************************************************/
380static HRESULT DetectWDDMDriver()
381{
382 HRESULT hr = S_OK;
383 HMODULE hModule = NULL;
384
385 // Manually load the d3d9.dll library. If the library couldn't be loaded then we obviously won't be able
386 // to try calling the function so just return.
387 hr = LoadSystemLibrary(L"d3d9.dll", &hModule);
388 if (E_MODNOTFOUND == hr)
389 {
390 TraceError(hr, "Unable to load DirectX APIs, skipping WDDM driver check.");
391 ExitFunction1(hr = S_OK);
392 }
393 ExitOnFailure(hr, "Failed to the load the existing DirectX APIs.");
394
395 // Obtain the address of the Direct3DCreate9Ex function. If this fails we know it isn't a WDDM
396 // driver so just exit.
397 const void* Direct3DCreate9ExPtr = ::GetProcAddress(hModule, "Direct3DCreate9Ex");
398 ExitOnNull(Direct3DCreate9ExPtr, hr, S_OK, "Unable to load Direct3DCreateEx function, so the driver is not a WDDM driver.");
399
400 // At this point we know it's a WDDM driver so set the property.
401 hr = WcaSetIntProperty(L"WIX_WDDM_DRIVER_PRESENT", 1);
402 ExitOnFailure(hr, "Failed write property");
403
404LExit:
405 if (NULL != hModule)
406 {
407 FreeLibrary(hModule);
408 }
409
410 return hr;
411}
412
413/********************************************************************
414DetectIsCompositionEnabled
415
416 Set a property based on the return value of DwmIsCompositionEnabled().
417********************************************************************/
418static HRESULT DetectIsCompositionEnabled()
419{
420 HRESULT hr = S_OK;
421 HMODULE hModule = NULL;
422 BOOL compositionState = false;
423
424 // Manually load the d3d9.dll library. If the library can't load it's likely because we are not on a Vista
425 // OS. Just return ok, and the property won't get set.
426 hr = LoadSystemLibrary(L"dwmapi.dll", &hModule);
427 if (E_MODNOTFOUND == hr)
428 {
429 TraceError(hr, "Unable to load Vista desktop window manager APIs, skipping Composition Enabled check.");
430 ExitFunction1(hr = S_OK);
431 }
432 ExitOnFailure(hr, "Failed to load the existing window manager APIs.");
433
434 // If for some reason we can't get the function pointer that's ok, just return.
435 typedef HRESULT (WINAPI *DWMISCOMPOSITIONENABLEDPTR)(BOOL*);
436 DWMISCOMPOSITIONENABLEDPTR DwmIsCompositionEnabledPtr = (DWMISCOMPOSITIONENABLEDPTR)::GetProcAddress(hModule, "DwmIsCompositionEnabled");
437 ExitOnNull(hModule, hr, S_OK, "Unable to obtain function information, skipping Composition Enabled check.");
438
439 hr = DwmIsCompositionEnabledPtr(&compositionState);
440 ExitOnFailure(hr, "Failed to retrieve Composition state");
441
442 if (compositionState)
443 {
444 hr = WcaSetIntProperty(L"WIX_DWM_COMPOSITION_ENABLED", 1);
445 ExitOnFailure(hr, "Failed write property");
446 }
447
448LExit:
449 if (NULL != hModule)
450 {
451 FreeLibrary(hModule);
452 }
453 return hr;
454}
455
456/********************************************************************
457WixQueryOsDriverInfo - entry point for WixQueryOsDriverInfo custom action
458
459 Called as Type 1 custom action (DLL from the Binary table) from
460 Windows Installer to set properties about drivers installed on
461 the target machine
462********************************************************************/
463extern "C" UINT __stdcall WixQueryOsDriverInfo(
464 __in MSIHANDLE hInstall
465 )
466{
467 HRESULT hr = S_OK;
468 DWORD er = ERROR_SUCCESS;
469
470 hr = WcaInitialize(hInstall, "WixQueryOsDriverInfo");
471 ExitOnFailure(hr, "WixQueryOsDriverInfo failed to initialize");
472
473 // Detect the WDDM driver status
474 hr = DetectWDDMDriver();
475 ExitOnFailure(hr, "Failed to detect WIX_WDDM_DRIVER_PRESENT");
476
477 // Detect whether composition is enabled
478 hr = DetectIsCompositionEnabled();
479 ExitOnFailure(hr, "Failed to detect WIX_DWM_COMPOSITION_ENABLED");
480
481LExit:
482 if (FAILED(hr))
483 {
484 er = ERROR_INSTALL_FAILURE;
485 }
486 return WcaFinalize(er);
487}
diff --git a/src/ext/Util/ca/RemoveFoldersEx.cpp b/src/ext/Util/ca/RemoveFoldersEx.cpp
new file mode 100644
index 00000000..cbc7f4bb
--- /dev/null
+++ b/src/ext/Util/ca/RemoveFoldersEx.cpp
@@ -0,0 +1,243 @@
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
5LPCWSTR vcsRemoveFolderExQuery =
6 L"SELECT `Wix4RemoveFolderEx`, `Component_`, `Property`, `InstallMode`, `WixRemoveFolderEx`.`Condition`, `Component`.`Attributes`"
7 L"FROM `Wix4RemoveFolderEx``,`Component` "
8 L"WHERE `Wix4RemoveFolderEx`.`Component_`=`Component`.`Component`";
9enum eRemoveFolderExQuery { rfqId = 1, rfqComponent, rfqProperty, rfqMode, rfqCondition, rfqComponentAttributes };
10
11static HRESULT RecursePath(
12 __in_z LPCWSTR wzPath,
13 __in_z LPCWSTR wzId,
14 __in_z LPCWSTR wzComponent,
15 __in_z LPCWSTR wzProperty,
16 __in int iMode,
17 __in BOOL fDisableWow64Redirection,
18 __inout DWORD* pdwCounter,
19 __inout MSIHANDLE* phTable,
20 __inout MSIHANDLE* phColumns
21 )
22{
23 HRESULT hr = S_OK;
24 DWORD er;
25 LPWSTR sczSearch = NULL;
26 LPWSTR sczProperty = NULL;
27 HANDLE hFind = INVALID_HANDLE_VALUE;
28 WIN32_FIND_DATAW wfd;
29 LPWSTR sczNext = NULL;
30
31 if (fDisableWow64Redirection)
32 {
33 hr = WcaDisableWow64FSRedirection();
34 ExitOnFailure(hr, "Custom action was told to act on a 64-bit component, but was unable to disable filesystem redirection through the Wow64 API.");
35 }
36
37 // First recurse down to all the child directories.
38 hr = StrAllocFormatted(&sczSearch, L"%s*", wzPath);
39 ExitOnFailure(hr, "Failed to allocate file search string in path: %S", wzPath);
40
41 hFind = ::FindFirstFileW(sczSearch, &wfd);
42 if (INVALID_HANDLE_VALUE == hFind)
43 {
44 er = ::GetLastError();
45 if (ERROR_PATH_NOT_FOUND == er)
46 {
47 WcaLog(LOGMSG_STANDARD, "Search path not found: %ls; skipping", sczSearch);
48 ExitFunction1(hr = S_FALSE);
49 }
50 else
51 {
52 hr = HRESULT_FROM_WIN32(er);
53 }
54 ExitOnFailure(hr, "Failed to find all files in path: %S", wzPath);
55 }
56
57 do
58 {
59 // Skip files and the dot directories.
60 if (FILE_ATTRIBUTE_DIRECTORY != (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) || L'.' == wfd.cFileName[0] && (L'\0' == wfd.cFileName[1] || (L'.' == wfd.cFileName[1] && L'\0' == wfd.cFileName[2])))
61 {
62 continue;
63 }
64
65 hr = StrAllocFormatted(&sczNext, L"%s%s\\", wzPath, wfd.cFileName);
66 ExitOnFailure(hr, "Failed to concat filename '%S' to string: %S", wfd.cFileName, wzPath);
67
68 // Don't re-disable redirection; if it was necessary, we've already done it.
69 hr = RecursePath(sczNext, wzId, wzComponent, wzProperty, iMode, FALSE, pdwCounter, phTable, phColumns);
70 ExitOnFailure(hr, "Failed to recurse path: %S", sczNext);
71 } while (::FindNextFileW(hFind, &wfd));
72
73 er = ::GetLastError();
74 if (ERROR_NO_MORE_FILES == er)
75 {
76 hr = S_OK;
77 }
78 else
79 {
80 hr = HRESULT_FROM_WIN32(er);
81 ExitOnFailure(hr, "Failed while looping through files in directory: %S", wzPath);
82 }
83
84 // Finally, set a property that points at our path.
85 hr = StrAllocFormatted(&sczProperty, L"_%s_%u", wzProperty, *pdwCounter);
86 ExitOnFailure(hr, "Failed to allocate Property for RemoveFile table with property: %S.", wzProperty);
87
88 ++(*pdwCounter);
89
90 hr = WcaSetProperty(sczProperty, wzPath);
91 ExitOnFailure(hr, "Failed to set Property: %S with path: %S", sczProperty, wzPath);
92
93 // Add the row to remove any files and another row to remove the folder.
94 hr = WcaAddTempRecord(phTable, phColumns, L"RemoveFile", NULL, 1, 5, L"RfxFiles", wzComponent, L"*.*", sczProperty, iMode);
95 ExitOnFailure(hr, "Failed to add row to remove all files for Wix4RemoveFolderEx row: %ls under path: %ls", wzId, wzPath);
96
97 hr = WcaAddTempRecord(phTable, phColumns, L"RemoveFile", NULL, 1, 5, L"RfxFolder", wzComponent, NULL, sczProperty, iMode);
98 ExitOnFailure(hr, "Failed to add row to remove folder for Wix4RemoveFolderEx row: %ls under path: %ls", wzId, wzPath);
99
100LExit:
101 if (INVALID_HANDLE_VALUE != hFind)
102 {
103 ::FindClose(hFind);
104 }
105
106 if (fDisableWow64Redirection)
107 {
108 WcaRevertWow64FSRedirection();
109 }
110
111 ReleaseStr(sczNext);
112 ReleaseStr(sczProperty);
113 ReleaseStr(sczSearch);
114 return hr;
115}
116
117extern "C" UINT WINAPI WixRemoveFoldersEx(
118 __in MSIHANDLE hInstall
119 )
120{
121 //AssertSz(FALSE, "debug WixRemoveFoldersEx");
122
123 HRESULT hr = S_OK;
124 PMSIHANDLE hView;
125 PMSIHANDLE hRec;
126 LPWSTR sczId = NULL;
127 LPWSTR sczComponent = NULL;
128 LPWSTR sczProperty = NULL;
129 LPWSTR sczCondition = NULL;
130 LPWSTR sczPath = NULL;
131 LPWSTR sczExpandedPath = NULL;
132 int iMode = 0;
133 int iComponentAttributes;
134 BOOL f64BitComponent = FALSE;
135 DWORD dwCounter = 0;
136 DWORD_PTR cchLen = 0;
137 MSIHANDLE hTable = NULL;
138 MSIHANDLE hColumns = NULL;
139
140 hr = WcaInitialize(hInstall, "WixRemoveFoldersEx");
141 ExitOnFailure(hr, "Failed to initialize WixRemoveFoldersEx.");
142
143 WcaInitializeWow64();
144
145 // anything to do?
146 if (S_OK != WcaTableExists(L"Wix4RemoveFolderEx"))
147 {
148 WcaLog(LOGMSG_STANDARD, "Wix4RemoveFolderEx table doesn't exist, so there are no folders to remove.");
149 ExitFunction();
150 }
151
152 // query and loop through all the remove folders exceptions
153 hr = WcaOpenExecuteView(vcsRemoveFolderExQuery, &hView);
154 ExitOnFailure(hr, "Failed to open view on Wix4RemoveFolderEx table");
155
156 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
157 {
158 hr = WcaGetRecordString(hRec, rfqId, &sczId);
159 ExitOnFailure(hr, "Failed to get remove folder identity.");
160
161 hr = WcaGetRecordString(hRec, rfqCondition, &sczCondition);
162 ExitOnFailure(hr, "Failed to get remove folder condition.");
163
164 if (sczCondition && *sczCondition)
165 {
166 MSICONDITION condition = ::MsiEvaluateConditionW(hInstall, sczCondition);
167 if (MSICONDITION_TRUE == condition)
168 {
169 WcaLog(LOGMSG_STANDARD, "True condition for row %S: %S; processing.", sczId, sczCondition);
170 }
171 else
172 {
173 WcaLog(LOGMSG_STANDARD, "False or invalid condition for row %S: %S; skipping.", sczId, sczCondition);
174 continue;
175 }
176 }
177
178 hr = WcaGetRecordString(hRec, rfqComponent, &sczComponent);
179 ExitOnFailure(hr, "Failed to get remove folder component.");
180
181 hr = WcaGetRecordString(hRec, rfqProperty, &sczProperty);
182 ExitOnFailure(hr, "Failed to get remove folder property.");
183
184 hr = WcaGetRecordInteger(hRec, rfqMode, &iMode);
185 ExitOnFailure(hr, "Failed to get remove folder mode");
186
187 hr = WcaGetProperty(sczProperty, &sczPath);
188 ExitOnFailure(hr, "Failed to resolve remove folder property: %S for row: %S", sczProperty, sczId);
189
190 hr = WcaGetRecordInteger(hRec, rfqComponentAttributes, &iComponentAttributes);
191 ExitOnFailure(hr, "failed to get component attributes for row: %ls", sczId);
192
193 f64BitComponent = iComponentAttributes & msidbComponentAttributes64bit;
194
195 // fail early if the property isn't set as you probably don't want your installers trying to delete SystemFolder
196 // StringCchLengthW succeeds only if the string is zero characters plus 1 for the terminating null
197 hr = ::StringCchLengthW(sczPath, 1, reinterpret_cast<UINT_PTR*>(&cchLen));
198 if (SUCCEEDED(hr))
199 {
200 ExitOnFailure(hr = E_INVALIDARG, "Missing folder property: %S for row: %S", sczProperty, sczId);
201 }
202
203 hr = PathExpand(&sczExpandedPath, sczPath, PATH_EXPAND_ENVIRONMENT);
204 ExitOnFailure(hr, "Failed to expand path: %S for row: %S", sczPath, sczId);
205
206 hr = PathBackslashTerminate(&sczExpandedPath);
207 ExitOnFailure(hr, "Failed to backslash-terminate path: %S", sczExpandedPath);
208
209 WcaLog(LOGMSG_STANDARD, "Recursing path: %S for row: %S.", sczExpandedPath, sczId);
210 hr = RecursePath(sczExpandedPath, sczId, sczComponent, sczProperty, iMode, f64BitComponent, &dwCounter, &hTable, &hColumns);
211 ExitOnFailure(hr, "Failed while navigating path: %S for row: %S", sczPath, sczId);
212 }
213
214 // reaching the end of the list is actually a good thing, not an error
215 if (E_NOMOREITEMS == hr)
216 {
217 hr = S_OK;
218 }
219 ExitOnFailure(hr, "Failure occured while processing Wix4RemoveFolderEx table");
220
221LExit:
222 WcaFinalizeWow64();
223
224 if (hColumns)
225 {
226 ::MsiCloseHandle(hColumns);
227 }
228
229 if (hTable)
230 {
231 ::MsiCloseHandle(hTable);
232 }
233
234 ReleaseStr(sczExpandedPath);
235 ReleaseStr(sczPath);
236 ReleaseStr(sczProperty);
237 ReleaseStr(sczComponent);
238 ReleaseStr(sczCondition);
239 ReleaseStr(sczId);
240
241 DWORD er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
242 return WcaFinalize(er);
243}
diff --git a/src/ext/Util/ca/RemoveRegistryKeysEx.cpp b/src/ext/Util/ca/RemoveRegistryKeysEx.cpp
new file mode 100644
index 00000000..478c0779
--- /dev/null
+++ b/src/ext/Util/ca/RemoveRegistryKeysEx.cpp
@@ -0,0 +1,114 @@
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
5LPCWSTR vcsRemoveRegistryKeyExQuery =
6 L"SELECT `Wix4RemoveRegistryKeyEx`, `Component_`, `Root`, `Key`, `InstallMode`, `Condition` FROM `Wix4RemoveRegistryKeyEx`";
7enum eRemoveRegistryKeyExQuery { rrxqId = 1, rrxqComponent, rrxqRoot, rrxqKey, rrxqMode, rrxqCondition };
8
9extern "C" UINT WINAPI WixRemoveRegistryKeysEx(
10 __in MSIHANDLE hInstall
11)
12{
13 //AssertSz(FALSE, "debug WixRemoveRegistryKeyEx");
14
15 HRESULT hr = S_OK;
16 PMSIHANDLE hView;
17 PMSIHANDLE hRec;
18 LPWSTR sczId = NULL;
19 LPWSTR sczComponent = NULL;
20 LPWSTR sczCondition = NULL;
21 LPWSTR sczKey = NULL;
22 int iRoot = 0;
23 int iMode = 0;
24 MSIHANDLE hTable = NULL;
25 MSIHANDLE hColumns = NULL;
26
27 hr = WcaInitialize(hInstall, __FUNCTION__);
28 ExitOnFailure(hr, "Failed to initialize " __FUNCTION__);
29
30 // anything to do?
31 if (S_OK != WcaTableExists(L"Wix4RemoveRegistryKeyEx"))
32 {
33 WcaLog(LOGMSG_STANDARD, "Wix4RemoveRegistryKeyEx table doesn't exist, so there are no registry keys to remove.");
34 ExitFunction();
35 }
36
37 hr = WcaOpenExecuteView(vcsRemoveRegistryKeyExQuery, &hView);
38 ExitOnFailure(hr, "Failed to open view on Wix4RemoveRegistryKeyEx table");
39
40 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
41 {
42 hr = WcaGetRecordString(hRec, rrxqId, &sczId);
43 ExitOnFailure(hr, "Failed to get Wix4RemoveRegistryKeyEx identity.");
44
45 hr = WcaGetRecordString(hRec, rrxqCondition, &sczCondition);
46 ExitOnFailure(hr, "Failed to get Wix4RemoveRegistryKeyEx condition.");
47
48 if (sczCondition && *sczCondition)
49 {
50 MSICONDITION condition = ::MsiEvaluateConditionW(hInstall, sczCondition);
51 if (MSICONDITION_TRUE == condition)
52 {
53 WcaLog(LOGMSG_STANDARD, "True condition for row %S: %S; processing.", sczId, sczCondition);
54 }
55 else
56 {
57 WcaLog(LOGMSG_STANDARD, "False or invalid condition for row %S: %S; skipping.", sczId, sczCondition);
58 continue;
59 }
60 }
61
62 hr = WcaGetRecordString(hRec, rrxqComponent, &sczComponent);
63 ExitOnFailure(hr, "Failed to get Wix4RemoveRegistryKeyEx component.");
64
65 hr = WcaGetRecordInteger(hRec, rrxqRoot, &iRoot);
66 ExitOnFailure(hr, "Failed to get Wix4RemoveRegistryKeyEx root.");
67
68 hr = WcaGetRecordString(hRec, rrxqKey, &sczKey);
69 ExitOnFailure(hr, "Failed to get Wix4RemoveRegistryKeyEx key.");
70
71 hr = WcaGetRecordInteger(hRec, rrxqMode, &iMode);
72 ExitOnFailure(hr, "Failed to get Wix4RemoveRegistryKeyEx mode.");
73
74 switch (iMode)
75 {
76 case 1: // remove on install
77 WcaLog(LOGMSG_STANDARD, "Adding RemoveRegistry row: %ls/%d/%ls/-/%ls", sczId, iRoot, sczKey, sczComponent);
78 hr = WcaAddTempRecord(&hTable, &hColumns, L"RemoveRegistry", NULL, 0, 5, sczId, iRoot, sczKey, L"-", sczComponent);
79 ExitOnFailure(hr, "Failed to add RemoveRegistry row for remove-on-install Wix4RemoveRegistryKeyEx row: %ls:", sczId);
80 break;
81 case 2: // remove on uninstall
82 WcaLog(LOGMSG_STANDARD, "Adding Registry row: %ls/%d/%ls/-/null/%ls", sczId, iRoot, sczKey, sczComponent);
83 hr = WcaAddTempRecord(&hTable, &hColumns, L"Registry", NULL, 0, 6, sczId, iRoot, sczKey, L"-", NULL, sczComponent);
84 ExitOnFailure(hr, "Failed to add Registry row for remove-on-uninstall Wix4RemoveRegistryKeyEx row: %ls:", sczId);
85 break;
86 }
87 }
88
89 // reaching the end of the list is actually a good thing, not an error
90 if (E_NOMOREITEMS == hr)
91 {
92 hr = S_OK;
93 }
94 ExitOnFailure(hr, "Failure occured while processing Wix4RemoveRegistryKeyEx table.");
95
96LExit:
97 if (hColumns)
98 {
99 ::MsiCloseHandle(hColumns);
100 }
101
102 if (hTable)
103 {
104 ::MsiCloseHandle(hTable);
105 }
106
107 ReleaseStr(sczKey);
108 ReleaseStr(sczComponent);
109 ReleaseStr(sczCondition);
110 ReleaseStr(sczId);
111
112 DWORD er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
113 return WcaFinalize(er);
114}
diff --git a/src/ext/Util/ca/RestartManager.cpp b/src/ext/Util/ca/RestartManager.cpp
new file mode 100644
index 00000000..c31819c1
--- /dev/null
+++ b/src/ext/Util/ca/RestartManager.cpp
@@ -0,0 +1,185 @@
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#include <restartmanager.h>
5
6// Include space for the terminating null.
7#define CCH_SESSION_KEY CCH_RM_SESSION_KEY + 1
8
9enum eRmuResourceType
10{
11 etInvalid,
12 etFilename,
13 etApplication,
14 etServiceName,
15
16 // Mask types from Attributes.
17 etTypeMask = 0xf,
18};
19
20LPCWSTR vcsRestartResourceQuery =
21 L"SELECT `Wix4RestartResource`.`Wix4RestartResource`, `Wix4RestartResource`.`Component_`, `Wix4RestartResource`.`Resource`, `Wix4RestartResource`.`Attributes` "
22 L"FROM `Wix4RestartResource`";
23enum eRestartResourceQuery { rrqRestartResource = 1, rrqComponent, rrqResource, rrqAttributes };
24
25/********************************************************************
26WixRegisterRestartResources - Immediate CA to register resources with RM.
27
28Enumerates components before InstallValidate and registers resources
29to be restarted by Restart Manager if the component action
30is anything other than None.
31
32Do not disable file system redirection.
33
34********************************************************************/
35extern "C" UINT __stdcall WixRegisterRestartResources(
36 __in MSIHANDLE hInstall
37 )
38{
39 HRESULT hr = S_OK;
40 UINT er = ERROR_SUCCESS;
41
42 PMSIHANDLE hView = NULL;
43 PMSIHANDLE hRec = NULL;
44
45 LPWSTR wzSessionKey = NULL;
46 size_t cchSessionKey = 0;
47 PRMU_SESSION pSession = NULL;
48
49 LPWSTR wzRestartResource = NULL;
50 LPWSTR wzComponent = NULL;
51 LPWSTR wzResource = NULL;
52 int iAttributes = NULL;
53 BOOL fIsComponentNull = FALSE;
54 WCA_TODO todo = WCA_TODO_UNKNOWN;
55 int iType = etInvalid;
56
57 hr = WcaInitialize(hInstall, "WixRegisterRestartResources");
58 ExitOnFailure(hr, "Failed to initialize.");
59
60 // Skip if the table doesn't exist.
61 if (S_OK != WcaTableExists(L"Wix4RestartResource"))
62 {
63 WcaLog(LOGMSG_STANDARD, "The Wix4RestartResource table does not exist; there are no resources to register with Restart Manager.");
64 ExitFunction();
65 }
66
67 // Get the existing Restart Manager session if available.
68 hr = WcaGetProperty(L"MsiRestartManagerSessionKey", &wzSessionKey);
69 ExitOnFailure(hr, "Failed to get the MsiRestartManagerSessionKey property.");
70
71 hr = ::StringCchLengthW(wzSessionKey, CCH_SESSION_KEY, &cchSessionKey);
72 ExitOnFailure(hr, "Failed to get the MsiRestartManagerSessionKey string length.");
73
74 // Skip if the property doesn't exist.
75 if (0 == cchSessionKey)
76 {
77 WcaLog(LOGMSG_STANDARD, "The MsiRestartManagerSessionKey property is not available to join.");
78 ExitFunction();
79 }
80
81 // Join the existing Restart Manager session if supported.
82 hr = RmuJoinSession(&pSession, wzSessionKey);
83 if (E_MODNOTFOUND == hr)
84 {
85 WcaLog(LOGMSG_STANDARD, "The Restart Manager is not supported on this platform. Skipping.");
86 ExitFunction1(hr = S_OK);
87 }
88 else if (FAILED(hr))
89 {
90 WcaLog(LOGMSG_STANDARD, "Failed to join the existing Restart Manager session %ls.", wzSessionKey);
91 ExitFunction1(hr = S_OK);
92 }
93
94 // Loop through each record in the table.
95 hr = WcaOpenExecuteView(vcsRestartResourceQuery, &hView);
96 ExitOnFailure(hr, "Failed to open a view on the RestartResource table.");
97
98 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
99 {
100 hr = WcaGetRecordString(hRec, rrqRestartResource, &wzRestartResource);
101 ExitOnFailure(hr, "Failed to get the RestartResource field value.");
102
103 hr = WcaGetRecordString(hRec, rrqComponent, &wzComponent);
104 ExitOnFailure(hr, "Failed to get the Component_ field value.");
105
106 hr = WcaGetRecordFormattedString(hRec, rrqResource, &wzResource);
107 ExitOnFailure(hr, "Failed to get the Resource formatted field value.");
108
109 hr = WcaGetRecordInteger(hRec, rrqAttributes, &iAttributes);
110 ExitOnFailure(hr, "Failed to get the Attributes field value.");
111
112 fIsComponentNull = ::MsiRecordIsNull(hRec, rrqComponent);
113 todo = WcaGetComponentToDo(wzComponent);
114
115 // Only register resources for components that are null, or being installed, reinstalled, or uninstalled.
116 if (!fIsComponentNull && WCA_TODO_UNKNOWN == todo)
117 {
118 WcaLog(LOGMSG_VERBOSE, "Skipping resource %ls.", wzRestartResource);
119 continue;
120 }
121
122 // Get the type from Attributes and add to the Restart Manager.
123 iType = iAttributes & etTypeMask;
124 switch (iType)
125 {
126 case etFilename:
127 WcaLog(LOGMSG_VERBOSE, "Registering file name %ls with the Restart Manager.", wzResource);
128 hr = RmuAddFile(pSession, wzResource);
129 ExitOnFailure(hr, "Failed to register the file name with the Restart Manager session.");
130 break;
131
132 case etApplication:
133 WcaLog(LOGMSG_VERBOSE, "Registering process name %ls with the Restart Manager.", wzResource);
134 hr = RmuAddProcessesByName(pSession, wzResource);
135 if (E_NOTFOUND == hr)
136 {
137 // ERROR_ACCESS_DENIED was returned when trying to register this process.
138 // Since other instances may have been registered, log a message and continue the setup rather than failing.
139 WcaLog(LOGMSG_STANDARD, "The process, %ls, could not be registered with the Restart Manager (probably because the setup is not elevated and the process is in another user context). A reboot may be requested later.", wzResource);
140 hr = S_OK;
141 }
142 else
143 {
144 ExitOnFailure(hr, "Failed to register the process name with the Restart Manager session.");
145 }
146 break;
147
148 case etServiceName:
149 WcaLog(LOGMSG_VERBOSE, "Registering service name %ls with the Restart Manager.", wzResource);
150 hr = RmuAddService(pSession, wzResource);
151 ExitOnFailure(hr, "Failed to register the service name with the Restart Manager session.");
152 break;
153
154 default:
155 WcaLog(LOGMSG_VERBOSE, "The resource type %d for %ls is not supported and will not be registered.", iType, wzRestartResource);
156 break;
157 }
158 }
159
160 if (E_NOMOREITEMS == hr)
161 {
162 hr = S_OK;
163 }
164 ExitOnFailure(hr, "Failed while looping through all rows to register resources.");
165
166 // Register the resources and unjoin the session.
167 hr = RmuEndSession(pSession);
168 if (FAILED(hr))
169 {
170 WcaLog(LOGMSG_VERBOSE, "Failed to register the resources with the Restart Manager.");
171 ExitFunction1(hr = S_OK);
172 }
173
174LExit:
175 ReleaseStr(wzRestartResource);
176 ReleaseStr(wzComponent);
177 ReleaseStr(wzResource);
178
179 if (FAILED(hr))
180 {
181 er = ERROR_INSTALL_FAILURE;
182 }
183
184 return WcaFinalize(er);
185}
diff --git a/src/ext/Util/ca/TouchFile.cpp b/src/ext/Util/ca/TouchFile.cpp
new file mode 100644
index 00000000..e704f922
--- /dev/null
+++ b/src/ext/Util/ca/TouchFile.cpp
@@ -0,0 +1,308 @@
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
5LPCWSTR vcsTouchFileQuery = L"SELECT `Wix4TouchFile`, `Component_`, `Path`, `Attributes` FROM `Wix4TouchFile`";
6enum TOUCH_FILE_QUERY { tfqId = 1, tfqComponent, tfqPath, tfqTouchFileAttributes };
7
8enum TOUCH_FILE_ATTRIBUTE
9{
10 TOUCH_FILE_ATTRIBUTE_ON_INSTALL = 0x01,
11 TOUCH_FILE_ATTRIBUTE_ON_REINSTALL = 0x02,
12 TOUCH_FILE_ATTRIBUTE_ON_UNINSTALL = 0x04,
13 TOUCH_FILE_ATTRIBUTE_64BIT = 0x10,
14 TOUCH_FILE_ATTRIBUTE_VITAL = 0x20
15};
16
17
18static BOOL SetExistingFileModifiedTime(
19 __in_z LPCWSTR wzId,
20 __in_z LPCWSTR wzPath,
21 __in BOOL f64Bit,
22 __in FILETIME* pftModified
23 )
24{
25 HRESULT hr = S_OK;
26 BOOL fReenableFileSystemRedirection = FALSE;
27
28 if (f64Bit)
29 {
30 hr = WcaDisableWow64FSRedirection();
31 ExitOnFailure(hr, "Failed to disable 64-bit file system redirection to path: '%ls' for: %ls", wzPath, wzId);
32
33 fReenableFileSystemRedirection = TRUE;
34 }
35
36 hr = FileSetTime(wzPath, NULL, NULL, pftModified);
37
38LExit:
39 if (fReenableFileSystemRedirection)
40 {
41 WcaRevertWow64FSRedirection();
42 }
43
44 return SUCCEEDED(hr);
45}
46
47
48static HRESULT AddDataToCustomActionData(
49 __deref_inout_z LPWSTR* psczCustomActionData,
50 __in_z LPCWSTR wzId,
51 __in_z LPCWSTR wzPath,
52 __in int iTouchFileAttributes,
53 __in FILETIME ftModified
54 )
55{
56 HRESULT hr = S_OK;
57
58 hr = WcaWriteStringToCaData(wzId, psczCustomActionData);
59 ExitOnFailure(hr, "Failed to add touch file identity to custom action data.");
60
61 hr = WcaWriteStringToCaData(wzPath, psczCustomActionData);
62 ExitOnFailure(hr, "Failed to add touch file path to custom action data.");
63
64 hr = WcaWriteIntegerToCaData(iTouchFileAttributes, psczCustomActionData);
65 ExitOnFailure(hr, "Failed to add touch file attributes to custom action data.");
66
67 hr = WcaWriteIntegerToCaData(ftModified.dwHighDateTime, psczCustomActionData);
68 ExitOnFailure(hr, "Failed to add touch file high date/time to custom action data.");
69
70 hr = WcaWriteIntegerToCaData(ftModified.dwLowDateTime, psczCustomActionData);
71 ExitOnFailure(hr, "Failed to add touch file low date/time to custom action data.");
72
73LExit:
74 return hr;
75}
76
77
78static BOOL TryGetExistingFileModifiedTime(
79 __in_z LPCWSTR wzId,
80 __in_z LPCWSTR wzPath,
81 __in BOOL f64Bit,
82 __inout FILETIME* pftModified
83 )
84{
85 HRESULT hr = S_OK;
86 BOOL fReenableFileSystemRedirection = FALSE;
87
88 if (f64Bit)
89 {
90 hr = WcaDisableWow64FSRedirection();
91 ExitOnFailure(hr, "Failed to disable 64-bit file system redirection to path: '%ls' for: %ls", wzPath, wzId);
92
93 fReenableFileSystemRedirection = TRUE;
94 }
95
96 hr = FileGetTime(wzPath, NULL, NULL, pftModified);
97 if (E_PATHNOTFOUND == hr || E_FILENOTFOUND == hr)
98 {
99 // If the file doesn't exist yet there is nothing to rollback (i.e. file will probably be removed during rollback), so
100 // keep the error code but don't log anything.
101 }
102 else if (FAILED(hr))
103 {
104 WcaLog(LOGMSG_STANDARD, "Cannot access modified timestamp for file: '%ls' due to error: 0x%x. Continuing with out rollback for: %ls", wzPath, hr, wzId);
105 }
106
107LExit:
108 if (fReenableFileSystemRedirection)
109 {
110 WcaRevertWow64FSRedirection();
111 }
112
113 return SUCCEEDED(hr);
114}
115
116
117static HRESULT ProcessTouchFileTable(
118 __in BOOL fInstalling
119 )
120{
121 HRESULT hr = S_OK;
122
123 FILETIME ftModified = {};
124
125 PMSIHANDLE hView;
126 PMSIHANDLE hRec;
127
128 LPWSTR sczId = NULL;
129 LPWSTR sczComponent = NULL;
130 int iTouchFileAttributes = 0;
131 LPWSTR sczPath = NULL;
132
133 FILETIME ftRollbackModified = {};
134 LPWSTR sczRollbackData = NULL;
135 LPWSTR sczExecuteData = NULL;
136
137 if (S_OK != WcaTableExists(L"Wix4TouchFile"))
138 {
139 ExitFunction();
140 }
141
142 ::GetSystemTimeAsFileTime(&ftModified);
143
144 hr = WcaOpenExecuteView(vcsTouchFileQuery, &hView);
145 ExitOnFailure(hr, "Failed to open view on Wix4TouchFile table");
146
147 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
148 {
149 hr = WcaGetRecordString(hRec, tfqId, &sczId);
150 ExitOnFailure(hr, "Failed to get touch file identity.");
151
152 hr = WcaGetRecordString(hRec, tfqComponent, &sczComponent);
153 ExitOnFailure(hr, "Failed to get touch file component for: %ls", sczId);
154
155 hr = WcaGetRecordInteger(hRec, tfqTouchFileAttributes, &iTouchFileAttributes);
156 ExitOnFailure(hr, "Failed to get touch file attributes for: %ls", sczId);
157
158 WCA_TODO todo = WcaGetComponentToDo(sczComponent);
159
160 BOOL fOnInstall = fInstalling && WCA_TODO_INSTALL == todo && (iTouchFileAttributes & TOUCH_FILE_ATTRIBUTE_ON_INSTALL);
161 BOOL fOnReinstall = fInstalling && WCA_TODO_REINSTALL == todo && (iTouchFileAttributes & TOUCH_FILE_ATTRIBUTE_ON_REINSTALL);
162 BOOL fOnUninstall = !fInstalling && WCA_TODO_UNINSTALL == todo && (iTouchFileAttributes & TOUCH_FILE_ATTRIBUTE_ON_UNINSTALL);
163
164 if (fOnInstall || fOnReinstall || fOnUninstall)
165 {
166 hr = WcaGetRecordFormattedString(hRec, tfqPath, &sczPath);
167 ExitOnFailure(hr, "Failed to get touch file path for: %ls", sczId);
168
169 if (TryGetExistingFileModifiedTime(sczId, sczPath, (iTouchFileAttributes & TOUCH_FILE_ATTRIBUTE_64BIT), &ftRollbackModified))
170 {
171 hr = AddDataToCustomActionData(&sczRollbackData, sczId, sczPath, iTouchFileAttributes, ftRollbackModified);
172 ExitOnFailure(hr, "Failed to add to rollback custom action data for: %ls", sczId);
173 }
174
175 hr = AddDataToCustomActionData(&sczExecuteData, sczId, sczPath, iTouchFileAttributes, ftModified);
176 ExitOnFailure(hr, "Failed to add to execute custom action data for: %ls", sczId);
177 }
178 }
179
180 if (E_NOMOREITEMS == hr)
181 {
182 hr = S_OK;
183 }
184 ExitOnFailure(hr, "Failure occured while processing Wix4TouchFile table");
185
186 if (sczRollbackData)
187 {
188 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"RollbackTouchFile"), sczRollbackData, 0);
189 ExitOnFailure(hr, "Failed to schedule RollbackTouchFile");
190 }
191
192 if (sczExecuteData)
193 {
194 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"ExecuteTouchFile"), sczExecuteData, 0);
195 ExitOnFailure(hr, "Failed to schedule ExecuteTouchFile");
196 }
197
198LExit:
199 ReleaseStr(sczExecuteData);
200 ReleaseStr(sczRollbackData);
201 ReleaseStr(sczPath);
202 ReleaseStr(sczComponent);
203 ReleaseStr(sczId);
204
205 return hr;
206}
207
208
209extern "C" UINT WINAPI WixTouchFileDuringInstall(
210 __in MSIHANDLE hInstall
211 )
212{
213 //AssertSz(FALSE, "debug WixTouchFileDuringInstall");
214
215 HRESULT hr = S_OK;
216
217 hr = WcaInitialize(hInstall, "WixTouchFileDuringInstall");
218 ExitOnFailure(hr, "Failed to initialize WixTouchFileDuringInstall.");
219
220 hr = ProcessTouchFileTable(TRUE);
221
222LExit:
223 DWORD er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
224 return WcaFinalize(er);
225}
226
227
228extern "C" UINT WINAPI WixTouchFileDuringUninstall(
229 __in MSIHANDLE hInstall
230 )
231{
232 //AssertSz(FALSE, "debug WixTouchFileDuringUninstall");
233
234 HRESULT hr = S_OK;
235
236 hr = WcaInitialize(hInstall, "WixTouchFileDuringUninstall");
237 ExitOnFailure(hr, "Failed to initialize WixTouchFileDuringUninstall.");
238
239 hr = ProcessTouchFileTable(FALSE);
240
241LExit:
242 DWORD er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
243 return WcaFinalize(er);
244}
245
246
247extern "C" UINT WINAPI WixExecuteTouchFile(
248 __in MSIHANDLE hInstall
249 )
250{
251 HRESULT hr = S_OK;
252
253 LPWSTR sczData = NULL;
254 LPWSTR pwz = NULL;
255
256 LPWSTR sczId = NULL;
257 LPWSTR sczPath = NULL;
258 int iTouchFileAttributes = 0;
259 FILETIME ftModified = {};
260
261 hr = WcaInitialize(hInstall, "WixExecuteTouchFile");
262 ExitOnFailure(hr, "Failed to initialize WixExecuteTouchFile.");
263
264 hr = WcaGetProperty(L"CustomActionData", &sczData);
265 ExitOnFailure(hr, "Failed to get custom action data for WixExecuteTouchFile.");
266
267 pwz = sczData;
268
269 while (pwz && *pwz)
270 {
271 hr = WcaReadStringFromCaData(&pwz, &sczId);
272 ExitOnFailure(hr, "Failed to get touch file identity from custom action data.");
273
274 hr = WcaReadStringFromCaData(&pwz, &sczPath);
275 ExitOnFailure(hr, "Failed to get touch file path from custom action data for: %ls", sczId);
276
277 hr = WcaReadIntegerFromCaData(&pwz, &iTouchFileAttributes);
278 ExitOnFailure(hr, "Failed to get touch file attributes from custom action data for: %ls", sczId);
279
280 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&ftModified.dwHighDateTime));
281 ExitOnFailure(hr, "Failed to get touch file high date/time from custom action data for: %ls", sczId);
282
283 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&ftModified.dwLowDateTime));
284 ExitOnFailure(hr, "Failed to get touch file low date/time from custom action data for: %ls", sczId);
285
286 hr = SetExistingFileModifiedTime(sczId, sczPath, (iTouchFileAttributes & TOUCH_FILE_ATTRIBUTE_64BIT), &ftModified);
287 if (FAILED(hr))
288 {
289 if (iTouchFileAttributes & TOUCH_FILE_ATTRIBUTE_VITAL)
290 {
291 ExitOnFailure(hr, "Failed to touch file: '%ls' for: %ls", &sczPath, sczId);
292 }
293 else
294 {
295 WcaLog(LOGMSG_STANDARD, "Could not touch non-vital file: '%ls' for: %ls with error: 0x%x. Continuing...", sczPath, sczId, hr);
296 hr = S_OK;
297 }
298 }
299 }
300
301LExit:
302 ReleaseStr(sczPath);
303 ReleaseStr(sczId);
304 ReleaseStr(sczData);
305
306 DWORD er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
307 return WcaFinalize(er);
308}
diff --git a/src/ext/Util/ca/XmlConfig.cpp b/src/ext/Util/ca/XmlConfig.cpp
new file mode 100644
index 00000000..a1ec9d6f
--- /dev/null
+++ b/src/ext/Util/ca/XmlConfig.cpp
@@ -0,0 +1,1130 @@
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 XMLCONFIG_ELEMENT 0x00000001
6#define XMLCONFIG_VALUE 0x00000002
7#define XMLCONFIG_DOCUMENT 0x00000004
8#define XMLCONFIG_CREATE 0x00000010
9#define XMLCONFIG_DELETE 0x00000020
10#define XMLCONFIG_INSTALL 0x00000100
11#define XMLCONFIG_UNINSTALL 0x00000200
12#define XMLCONFIG_PRESERVE_MODIFIED 0x00001000
13
14enum eXmlAction
15{
16 xaUnknown = 0,
17 xaOpenFile,
18 xaOpenFilex64,
19 xaWriteValue,
20 xaWriteDocument,
21 xaDeleteValue,
22 xaCreateElement,
23 xaDeleteElement,
24};
25
26enum eXmlPreserveDate
27{
28 xdDontPreserve = 0,
29 xdPreserve
30};
31
32LPCWSTR vcsXmlConfigQuery =
33 L"SELECT `Wix4XmlConfig`.`Wix4XmlConfig`, `Wix4XmlConfig`.`File`, `Wix4XmlConfig`.`ElementId`, `Wix4XmlConfig`.`ElementPath`, `Wix4XmlConfig`.`VerifyPath`, `Wix4XmlConfig`.`Name`, "
34 L"`Wix4XmlConfig`.`Value`, `Wix4XmlConfig`.`Flags`, `Wix4XmlConfig`.`Component_`, `Component`.`Attributes` "
35 L"FROM `Wix4XmlConfig`,`Component` WHERE `Wix4XmlConfig`.`Component_`=`Component`.`Component` ORDER BY `File`, `Sequence`";
36enum eXmlConfigQuery { xfqXmlConfig = 1, xfqFile, xfqElementId, xfqElementPath, xfqVerifyPath, xfqName, xfqValue, xfqXmlFlags, xfqComponent, xfqCompAttributes };
37
38struct XML_CONFIG_CHANGE
39{
40 WCHAR wzId[MAX_DARWIN_KEY + 1];
41
42 WCHAR wzComponent[MAX_DARWIN_KEY + 1];
43 INSTALLSTATE isInstalled;
44 INSTALLSTATE isAction;
45
46 WCHAR wzFile[MAX_PATH];
47 LPWSTR pwzElementId;
48 LPWSTR pwzElementPath;
49 LPWSTR pwzVerifyPath;
50 WCHAR wzName[MAX_DARWIN_COLUMN];
51 LPWSTR pwzValue;
52 BOOL fInstalledFile;
53
54 int iXmlFlags;
55 int iCompAttributes;
56
57 XML_CONFIG_CHANGE* pxfcAdditionalChanges;
58 int cAdditionalChanges;
59
60 XML_CONFIG_CHANGE* pxfcPrev;
61 XML_CONFIG_CHANGE* pxfcNext;
62};
63
64static HRESULT FreeXmlConfigChangeList(
65 __in_opt XML_CONFIG_CHANGE* pxfcList
66 )
67{
68 HRESULT hr = S_OK;
69
70 XML_CONFIG_CHANGE* pxfcDelete;
71 while(pxfcList)
72 {
73 pxfcDelete = pxfcList;
74 pxfcList = pxfcList->pxfcNext;
75
76 if (pxfcDelete->pwzElementId)
77 {
78 hr = MemFree(pxfcDelete->pwzElementId);
79 ExitOnFailure(hr, "failed to free xml config element id in change list item");
80 }
81
82 if (pxfcDelete->pwzElementPath)
83 {
84 hr = MemFree(pxfcDelete->pwzElementPath);
85 ExitOnFailure(hr, "failed to free xml config element path in change list item");
86 }
87
88 if (pxfcDelete->pwzVerifyPath)
89 {
90 hr = MemFree(pxfcDelete->pwzVerifyPath);
91 ExitOnFailure(hr, "failed to free xml config verify path in change list item");
92 }
93
94 if (pxfcDelete->pwzValue)
95 {
96 hr = MemFree(pxfcDelete->pwzValue);
97 ExitOnFailure(hr, "failed to free xml config value in change list item");
98 }
99
100 hr = MemFree(pxfcDelete);
101 ExitOnFailure(hr, "failed to free xml config change list item");
102 }
103
104LExit:
105 return hr;
106}
107
108static HRESULT AddXmlConfigChangeToList(
109 __inout XML_CONFIG_CHANGE** ppxfcHead,
110 __inout XML_CONFIG_CHANGE** ppxfcTail
111 )
112{
113 Assert(ppxfcHead && ppxfcTail);
114
115 HRESULT hr = S_OK;
116
117 XML_CONFIG_CHANGE* pxfc = static_cast<XML_CONFIG_CHANGE*>(MemAlloc(sizeof(XML_CONFIG_CHANGE), TRUE));
118 ExitOnNull(pxfc, hr, E_OUTOFMEMORY, "failed to allocate memory for new xml file change list element");
119
120 // Add it to the end of the list
121 if (NULL == *ppxfcHead)
122 {
123 *ppxfcHead = pxfc;
124 *ppxfcTail = pxfc;
125 }
126 else
127 {
128 Assert(*ppxfcTail && (*ppxfcTail)->pxfcNext == NULL);
129 (*ppxfcTail)->pxfcNext = pxfc;
130 pxfc->pxfcPrev = *ppxfcTail;
131 *ppxfcTail = pxfc;
132 }
133
134LExit:
135 return hr;
136}
137
138
139static HRESULT ReadXmlConfigTable(
140 __inout XML_CONFIG_CHANGE** ppxfcHead,
141 __inout XML_CONFIG_CHANGE** ppxfcTail
142 )
143{
144 Assert(ppxfcHead && ppxfcTail);
145
146 HRESULT hr = S_OK;
147 UINT er = ERROR_SUCCESS;
148
149 PMSIHANDLE hView = NULL;
150 PMSIHANDLE hRec = NULL;
151
152 LPWSTR pwzData = NULL;
153
154 // loop through all the xml configurations
155 hr = WcaOpenExecuteView(vcsXmlConfigQuery, &hView);
156 ExitOnFailure(hr, "failed to open view on Wix4XmlConfig table");
157
158 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
159 {
160 hr = AddXmlConfigChangeToList(ppxfcHead, ppxfcTail);
161 ExitOnFailure(hr, "failed to add xml file change to list");
162
163 // Get record Id
164 hr = WcaGetRecordString(hRec, xfqXmlConfig, &pwzData);
165 ExitOnFailure(hr, "failed to get Wix4XmlConfig record Id");
166 hr = StringCchCopyW((*ppxfcTail)->wzId, countof((*ppxfcTail)->wzId), pwzData);
167 ExitOnFailure(hr, "failed to copy Wix4XmlConfig record Id");
168
169 // Get component name
170 hr = WcaGetRecordString(hRec, xfqComponent, &pwzData);
171 ExitOnFailure(hr, "failed to get component name for Wix4XmlConfig: %ls", (*ppxfcTail)->wzId);
172
173 // Get the component's state
174 if (pwzData && *pwzData)
175 {
176 hr = StringCchCopyW((*ppxfcTail)->wzComponent, countof((*ppxfcTail)->wzComponent), pwzData);
177 ExitOnFailure(hr, "failed to copy component id");
178
179 er = ::MsiGetComponentStateW(WcaGetInstallHandle(), (*ppxfcTail)->wzComponent, &(*ppxfcTail)->isInstalled, &(*ppxfcTail)->isAction);
180 ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "failed to get install state for component id");
181 }
182
183 // Get the xml file
184 hr = WcaGetRecordFormattedString(hRec, xfqFile, &pwzData);
185 ExitOnFailure(hr, "failed to get xml file for Wix4XmlConfig: %ls", (*ppxfcTail)->wzId);
186 hr = StringCchCopyW((*ppxfcTail)->wzFile, countof((*ppxfcTail)->wzFile), pwzData);
187 ExitOnFailure(hr, "failed to copy xml file path");
188
189 // Figure out if the file is already on the machine or if it's being installed
190 hr = WcaGetRecordString(hRec, xfqFile, &pwzData);
191 ExitOnFailure(hr, "failed to get xml file for Wix4XmlConfig: %ls", (*ppxfcTail)->wzId);
192 if (NULL != wcsstr(pwzData, L"[!") || NULL != wcsstr(pwzData, L"[#"))
193 {
194 (*ppxfcTail)->fInstalledFile = TRUE;
195 }
196
197 // Get the Wix4XmlConfig table flags
198 hr = WcaGetRecordInteger(hRec, xfqXmlFlags, &(*ppxfcTail)->iXmlFlags);
199 ExitOnFailure(hr, "failed to get Wix4XmlConfig flags for Wix4XmlConfig: %ls", (*ppxfcTail)->wzId);
200
201 // Get the Element Id
202 hr = WcaGetRecordFormattedString(hRec, xfqElementId, &(*ppxfcTail)->pwzElementId);
203 ExitOnFailure(hr, "failed to get Element Id for Wix4XmlConfig: %ls", (*ppxfcTail)->wzId);
204
205 // Get the Element Path
206 hr = WcaGetRecordFormattedString(hRec, xfqElementPath, &(*ppxfcTail)->pwzElementPath);
207 ExitOnFailure(hr, "failed to get Element Path for Wix4XmlConfig: %ls", (*ppxfcTail)->wzId);
208
209 // Get the Verify Path
210 hr = WcaGetRecordFormattedString(hRec, xfqVerifyPath, &(*ppxfcTail)->pwzVerifyPath);
211 ExitOnFailure(hr, "failed to get Verify Path for Wix4XmlConfig: %ls", (*ppxfcTail)->wzId);
212
213 // Get the name
214 hr = WcaGetRecordFormattedString(hRec, xfqName, &pwzData);
215 ExitOnFailure(hr, "failed to get Name for Wix4XmlConfig: %ls", (*ppxfcTail)->wzId);
216 hr = StringCchCopyW((*ppxfcTail)->wzName, countof((*ppxfcTail)->wzName), pwzData);
217 ExitOnFailure(hr, "failed to copy name of element");
218
219 // Get the value
220 hr = WcaGetRecordFormattedString(hRec, xfqValue, &pwzData);
221 ExitOnFailure(hr, "failed to get Value for Wix4XmlConfig: %ls", (*ppxfcTail)->wzId);
222 hr = StrAllocString(&(*ppxfcTail)->pwzValue, pwzData, 0);
223 ExitOnFailure(hr, "failed to allocate buffer for value");
224
225 // Get the component attributes
226 hr = WcaGetRecordInteger(hRec, xfqCompAttributes, &(*ppxfcTail)->iCompAttributes);
227 ExitOnFailure(hr, "failed to get component attributes for Wix4XmlConfig: %ls", (*ppxfcTail)->wzId);
228 }
229
230 // if we looped through all records all is well
231 if (E_NOMOREITEMS == hr)
232 {
233 hr = S_OK;
234 }
235 ExitOnFailure(hr, "failed while looping through all objects to secure");
236
237LExit:
238 ReleaseStr(pwzData);
239
240 return hr;
241}
242
243static HRESULT ProcessChanges(
244 __inout XML_CONFIG_CHANGE** ppxfcHead
245 )
246{
247 Assert(ppxfcHead && *ppxfcHead);
248 HRESULT hr = S_OK;
249
250 XML_CONFIG_CHANGE* pxfc = NULL;
251 XML_CONFIG_CHANGE* pxfcNext = NULL;
252 XML_CONFIG_CHANGE* pxfcCheck = NULL;
253 int cAdditionalChanges = 0;
254 XML_CONFIG_CHANGE* pxfcLast = NULL;
255
256 // If there's only one item in the list, none of this matters
257 if (pxfc && !pxfc->pxfcNext)
258 {
259 ExitFunction();
260 }
261
262 // Loop through the list
263 pxfc = *ppxfcHead;
264 while (pxfc)
265 {
266 // Keep track of where our next spot will be since our current node may be moved
267 pxfcNext = pxfc->pxfcNext;
268
269 // With each node, check to see if it's element path matches the Id of some other node in the list
270 pxfcCheck = *ppxfcHead;
271 while (pxfcCheck)
272 {
273 if (pxfc->pwzElementId)
274 {
275 if (0 == lstrcmpW(pxfc->pwzElementId, pxfcCheck->wzId)
276 && 0 == pxfc->iXmlFlags
277 && XMLCONFIG_CREATE & pxfcCheck->iXmlFlags
278 && XMLCONFIG_ELEMENT & pxfcCheck->iXmlFlags)
279 {
280 // We found a match. First, take it out of the current list
281 if (pxfc->pxfcPrev)
282 {
283 pxfc->pxfcPrev->pxfcNext = pxfc->pxfcNext;
284 }
285 else // it was the head. Update the head
286 {
287 *ppxfcHead = pxfc->pxfcNext;
288 }
289
290 if (pxfc->pxfcNext)
291 {
292 pxfc->pxfcNext->pxfcPrev = pxfc->pxfcPrev;
293 }
294
295 pxfc->pxfcNext = NULL;
296 pxfc->pxfcPrev = NULL;
297
298 // Now, add this node to the end of the matched node's additional changes list
299 if (!pxfcCheck->pxfcAdditionalChanges)
300 {
301 pxfcCheck->pxfcAdditionalChanges = pxfc;
302 pxfcCheck->cAdditionalChanges = 1;
303 }
304 else
305 {
306 pxfcLast = pxfcCheck->pxfcAdditionalChanges;
307 cAdditionalChanges = 1;
308 while (pxfcLast->pxfcNext)
309 {
310 pxfcLast = pxfcLast->pxfcNext;
311 ++cAdditionalChanges;
312 }
313 pxfcLast->pxfcNext = pxfc;
314 pxfc->pxfcPrev = pxfcLast;
315 pxfcCheck->cAdditionalChanges = ++cAdditionalChanges;
316 }
317 }
318 else
319 {
320 hr = E_NOTFOUND;
321 ExitOnRootFailure(hr, "failed to find matching ElementId: %ls", pxfc->pwzElementId);
322 }
323 }
324
325 pxfcCheck = pxfcCheck->pxfcNext;
326 }
327
328 pxfc = pxfcNext;
329 }
330
331LExit:
332
333 return hr;
334}
335
336
337static HRESULT BeginChangeFile(
338 __in LPCWSTR pwzFile,
339 __in int iCompAttributes,
340 __inout LPWSTR* ppwzCustomActionData
341 )
342{
343 Assert(pwzFile && *pwzFile && ppwzCustomActionData);
344
345 HRESULT hr = S_OK;
346 BOOL fIs64Bit = iCompAttributes & msidbComponentAttributes64bit;
347
348 LPBYTE pbData = NULL;
349 SIZE_T cbData = 0;
350
351 LPWSTR pwzRollbackCustomActionData = NULL;
352
353 if (fIs64Bit)
354 {
355 hr = WcaWriteIntegerToCaData((int)xaOpenFilex64, ppwzCustomActionData);
356 ExitOnFailure(hr, "failed to write 64-bit file indicator to custom action data");
357 }
358 else
359 {
360 hr = WcaWriteIntegerToCaData((int)xaOpenFile, ppwzCustomActionData);
361 ExitOnFailure(hr, "failed to write file indicator to custom action data");
362 }
363
364 hr = WcaWriteStringToCaData(pwzFile, ppwzCustomActionData);
365 ExitOnFailure(hr, "failed to write file to custom action data: %ls", pwzFile);
366
367 // If the file already exits, then we have to put it back the way it was on failure
368 if (FileExistsEx(pwzFile, NULL))
369 {
370 hr = FileRead(&pbData, &cbData, pwzFile);
371 ExitOnFailure(hr, "failed to read file: %ls", pwzFile);
372
373 // Set up the rollback for this file
374 hr = WcaWriteIntegerToCaData((int)fIs64Bit, &pwzRollbackCustomActionData);
375 ExitOnFailure(hr, "failed to write component bitness to rollback custom action data");
376
377 hr = WcaWriteStringToCaData(pwzFile, &pwzRollbackCustomActionData);
378 ExitOnFailure(hr, "failed to write file name to rollback custom action data: %ls", pwzFile);
379
380 hr = WcaWriteStreamToCaData(pbData, cbData, &pwzRollbackCustomActionData);
381 ExitOnFailure(hr, "failed to write file contents to rollback custom action data.");
382
383 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"ExecXmlConfigRollback"), pwzRollbackCustomActionData, COST_XMLFILE);
384 ExitOnFailure(hr, "failed to schedule ExecXmlConfigRollback for file: %ls", pwzFile);
385
386 ReleaseStr(pwzRollbackCustomActionData);
387 }
388LExit:
389 ReleaseMem(pbData);
390
391 return hr;
392}
393
394
395static HRESULT WriteChangeData(
396 __in XML_CONFIG_CHANGE* pxfc,
397 __in eXmlAction action,
398 __inout LPWSTR* ppwzCustomActionData
399 )
400{
401 Assert(pxfc && ppwzCustomActionData);
402
403 HRESULT hr = S_OK;
404 XML_CONFIG_CHANGE* pxfcAdditionalChanges = NULL;
405 LPCWSTR wzElementPath = pxfc->pwzElementId ? pxfc->pwzElementId : pxfc->pwzElementPath;
406
407 hr = WcaWriteStringToCaData(wzElementPath, ppwzCustomActionData);
408 ExitOnFailure(hr, "failed to write ElementPath to custom action data: %ls", wzElementPath);
409
410 hr = WcaWriteStringToCaData(pxfc->pwzVerifyPath, ppwzCustomActionData);
411 ExitOnFailure(hr, "failed to write VerifyPath to custom action data: %ls", pxfc->pwzVerifyPath);
412
413 hr = WcaWriteStringToCaData(pxfc->wzName, ppwzCustomActionData);
414 ExitOnFailure(hr, "failed to write Name to custom action data: %ls", pxfc->wzName);
415
416 hr = WcaWriteStringToCaData(pxfc->pwzValue, ppwzCustomActionData);
417 ExitOnFailure(hr, "failed to write Value to custom action data: %ls", pxfc->pwzValue);
418
419 if (pxfc->iXmlFlags & XMLCONFIG_CREATE && pxfc->iXmlFlags & XMLCONFIG_ELEMENT && xaCreateElement == action && pxfc->pxfcAdditionalChanges)
420 {
421 hr = WcaWriteIntegerToCaData(pxfc->cAdditionalChanges, ppwzCustomActionData);
422 ExitOnFailure(hr, "failed to write additional changes value to custom action data");
423
424 pxfcAdditionalChanges = pxfc->pxfcAdditionalChanges;
425 while (pxfcAdditionalChanges)
426 {
427 Assert((0 == lstrcmpW(pxfcAdditionalChanges->wzComponent, pxfc->wzComponent)) && 0 == pxfcAdditionalChanges->iXmlFlags && (0 == lstrcmpW(pxfcAdditionalChanges->wzFile, pxfc->wzFile)));
428
429 hr = WcaWriteStringToCaData(pxfcAdditionalChanges->wzName, ppwzCustomActionData);
430 ExitOnFailure(hr, "failed to write Name to custom action data: %ls", pxfc->wzName);
431
432 hr = WcaWriteStringToCaData(pxfcAdditionalChanges->pwzValue, ppwzCustomActionData);
433 ExitOnFailure(hr, "failed to write Value to custom action data: %ls", pxfc->pwzValue);
434
435 pxfcAdditionalChanges = pxfcAdditionalChanges->pxfcNext;
436 }
437 }
438 else
439 {
440 hr = WcaWriteIntegerToCaData(0, ppwzCustomActionData);
441 ExitOnFailure(hr, "failed to write additional changes value to custom action data");
442 }
443
444LExit:
445 return hr;
446}
447
448
449/******************************************************************
450 SchedXmlConfig - entry point for XmlConfig Custom Action
451
452********************************************************************/
453extern "C" UINT __stdcall SchedXmlConfig(
454 __in MSIHANDLE hInstall
455 )
456{
457// AssertSz(FALSE, "debug SchedXmlConfig");
458
459 HRESULT hr = S_OK;
460 UINT er = ERROR_SUCCESS;
461
462 LPWSTR pwzCurrentFile = NULL;
463 BOOL fCurrentFileChanged = FALSE;
464
465 PMSIHANDLE hView = NULL;
466 PMSIHANDLE hRec = NULL;
467
468 XML_CONFIG_CHANGE* pxfcHead = NULL;
469 XML_CONFIG_CHANGE* pxfcTail = NULL; // TODO: do we need this any more?
470 XML_CONFIG_CHANGE* pxfc = NULL;
471
472 eXmlAction xa = xaUnknown;
473 eXmlPreserveDate xd;
474
475 LPWSTR pwzCustomActionData = NULL;
476
477 DWORD cFiles = 0;
478
479 // initialize
480 hr = WcaInitialize(hInstall, "SchedXmlConfig");
481 ExitOnFailure(hr, "failed to initialize");
482
483 hr = ReadXmlConfigTable(&pxfcHead, &pxfcTail);
484 MessageExitOnFailure(hr, msierrXmlConfigFailedRead, "failed to read Wix4XmlConfig table");
485
486 hr = ProcessChanges(&pxfcHead);
487 ExitOnFailure(hr, "failed to process Wix4XmlConfig changes");
488
489 // loop through all the xml configurations
490 for (pxfc = pxfcHead; pxfc; pxfc = pxfc->pxfcNext)
491 {
492 // If this is a different file, or the first file...
493 if (NULL == pwzCurrentFile || 0 != lstrcmpW(pwzCurrentFile, pxfc->wzFile))
494 {
495 // Remember the file we're currently working on
496 hr = StrAllocString(&pwzCurrentFile, pxfc->wzFile, 0);
497 ExitOnFailure(hr, "failed to copy file name");
498
499 fCurrentFileChanged = TRUE;
500 }
501
502 //
503 // Figure out what action to take
504 //
505 xa = xaUnknown;
506
507 // If it's being installed or reinstalled or uninstalled and that matches
508 // what we are doing then calculate the right action.
509 if ((XMLCONFIG_INSTALL & pxfc->iXmlFlags && (WcaIsInstalling(pxfc->isInstalled, pxfc->isAction) || WcaIsReInstalling(pxfc->isInstalled, pxfc->isAction))) ||
510 (XMLCONFIG_UNINSTALL & pxfc->iXmlFlags && WcaIsUninstalling(pxfc->isInstalled, pxfc->isAction)))
511 {
512 if (XMLCONFIG_CREATE & pxfc->iXmlFlags && XMLCONFIG_ELEMENT & pxfc->iXmlFlags)
513 {
514 xa = xaCreateElement;
515 }
516 else if (XMLCONFIG_DELETE & pxfc->iXmlFlags && XMLCONFIG_ELEMENT & pxfc->iXmlFlags)
517 {
518 xa = xaDeleteElement;
519 }
520 else if (XMLCONFIG_DELETE & pxfc->iXmlFlags && XMLCONFIG_VALUE & pxfc->iXmlFlags)
521 {
522 xa = xaDeleteValue;
523 }
524 else if (XMLCONFIG_CREATE & pxfc->iXmlFlags && XMLCONFIG_VALUE & pxfc->iXmlFlags)
525 {
526 xa = xaWriteValue;
527 }
528 else if (XMLCONFIG_CREATE & pxfc->iXmlFlags && XMLCONFIG_DOCUMENT & pxfc->iXmlFlags)
529 {
530 xa = xaWriteDocument;
531 }
532 else if (XMLCONFIG_DELETE & pxfc->iXmlFlags && XMLCONFIG_DOCUMENT & pxfc->iXmlFlags)
533 {
534 hr = E_INVALIDARG;
535 ExitOnFailure(hr, "Invalid flag configuration. Cannot delete a fragment node.");
536 }
537 }
538
539 if (XMLCONFIG_PRESERVE_MODIFIED & pxfc->iXmlFlags)
540 {
541 xd = xdPreserve;
542 }
543 else
544 {
545 xd= xdDontPreserve;
546 }
547
548 if (xaUnknown != xa)
549 {
550 if (fCurrentFileChanged)
551 {
552 hr = BeginChangeFile(pwzCurrentFile, pxfc->iCompAttributes, &pwzCustomActionData);
553 ExitOnFailure(hr, "failed to begin file change for file: %ls", pwzCurrentFile);
554
555 fCurrentFileChanged = FALSE;
556 ++cFiles;
557 }
558
559 hr = WcaWriteIntegerToCaData((int)xa, &pwzCustomActionData);
560 ExitOnFailure(hr, "failed to write action indicator custom action data");
561
562 hr = WcaWriteIntegerToCaData((int)xd, &pwzCustomActionData);
563 ExitOnFailure(hr, "failed to write Preserve Date indicator to custom action data");
564
565 hr = WriteChangeData(pxfc, xa, &pwzCustomActionData);
566 ExitOnFailure(hr, "failed to write change data");
567 }
568 }
569
570 // If we looped through all records all is well
571 if (E_NOMOREITEMS == hr)
572 {
573 hr = S_OK;
574 }
575 ExitOnFailure(hr, "failed while looping through all objects to secure");
576
577 // Schedule the custom action and add to progress bar
578 if (pwzCustomActionData && *pwzCustomActionData)
579 {
580 Assert(0 < cFiles);
581
582 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"ExecXmlConfig"), pwzCustomActionData, cFiles * COST_XMLFILE);
583 ExitOnFailure(hr, "failed to schedule ExecXmlConfig action");
584 }
585
586LExit:
587 ReleaseStr(pwzCurrentFile);
588 ReleaseStr(pwzCustomActionData);
589
590 FreeXmlConfigChangeList(pxfcHead);
591
592 if (FAILED(hr))
593 {
594 er = ERROR_INSTALL_FAILURE;
595 }
596 return WcaFinalize(er);
597}
598
599
600/******************************************************************
601 ExecXmlConfig - entry point for XmlConfig Custom Action
602
603*******************************************************************/
604extern "C" UINT __stdcall ExecXmlConfig(
605 __in MSIHANDLE hInstall
606 )
607{
608 //AssertSz(FALSE, "debug ExecXmlConfig");
609 HRESULT hr = S_OK;
610 HRESULT hrOpenFailure = S_OK;
611 UINT er = ERROR_SUCCESS;
612
613#ifndef _WIN64
614 BOOL fIsFSRedirectDisabled = FALSE;
615#endif
616 BOOL fPreserveDate = FALSE;
617
618 LPWSTR pwzCustomActionData = NULL;
619 LPWSTR pwzData = NULL;
620 LPWSTR pwzFile = NULL;
621 LPWSTR pwzElementPath = NULL;
622 LPWSTR pwzVerifyPath = NULL;
623 LPWSTR pwzName = NULL;
624 LPWSTR pwzValue = NULL;
625 LPWSTR pwz = NULL;
626 int cAdditionalChanges = 0;
627
628 IXMLDOMDocument* pixd = NULL;
629 IXMLDOMNode* pixn = NULL;
630 IXMLDOMNode* pixnVerify = NULL;
631 IXMLDOMNode* pixnNewNode = NULL;
632 IXMLDOMNode* pixnRemovedChild = NULL;
633
634 IXMLDOMDocument* pixdNew = NULL;
635 IXMLDOMElement* pixeNew = NULL;
636
637 FILETIME ft;
638
639 int id = IDRETRY;
640
641 eXmlAction xa;
642 eXmlPreserveDate xd;
643
644 // initialize
645 hr = WcaInitialize(hInstall, "ExecXmlConfig");
646 ExitOnFailure(hr, "failed to initialize");
647
648 hr = XmlInitialize();
649 ExitOnFailure(hr, "failed to initialize xml utilities");
650
651 hr = WcaGetProperty( L"CustomActionData", &pwzCustomActionData);
652 ExitOnFailure(hr, "failed to get CustomActionData");
653
654 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzCustomActionData);
655
656 pwz = pwzCustomActionData;
657
658 hr = WcaReadIntegerFromCaData(&pwz, (int*) &xa);
659 ExitOnFailure(hr, "failed to process CustomActionData");
660
661#ifndef _WIN64
662 // Initialize the Wow64 API - store the result in fWow64APIPresent
663 // If it fails, this doesn't warrant an error yet, because we only need the Wow64 API in some cases
664 WcaInitializeWow64();
665 BOOL fIsWow64Process = WcaIsWow64Process();
666#endif
667
668 if (xaOpenFile != xa && xaOpenFilex64 != xa)
669 {
670 ExitOnFailure(hr = E_INVALIDARG, "invalid custom action data");
671 }
672
673 // loop through all the passed in data
674 while (pwz && *pwz)
675 {
676 hr = WcaReadStringFromCaData(&pwz, &pwzFile);
677 ExitOnFailure(hr, "failed to read file name from custom action data");
678
679 // Default to not preserve date, preserve it if any modifications require us to
680 fPreserveDate = FALSE;
681
682 // Open the file
683 ReleaseNullObject(pixd);
684
685#ifndef _WIN64
686 if (xaOpenFilex64 == xa)
687 {
688 if (!fIsWow64Process)
689 {
690 hr = E_NOTIMPL;
691 ExitOnFailure(hr, "Custom action was told to act on a 64-bit component, but the custom action process is not running in WOW.");
692 }
693
694 hr = WcaDisableWow64FSRedirection();
695 ExitOnFailure(hr, "Custom action was told to act on a 64-bit component, but was unable to disable filesystem redirection through the Wow64 API.");
696
697 fIsFSRedirectDisabled = TRUE;
698 }
699#endif
700
701 hr = XmlLoadDocumentFromFileEx(pwzFile, XML_LOAD_PRESERVE_WHITESPACE, &pixd);
702 if (FAILED(hr))
703 {
704 // Ignore the return code for now. If they try to add something, we'll fail the install. If all they do is remove stuff then it doesn't matter.
705 hrOpenFailure = hr;
706 hr = S_OK;
707 }
708 else
709 {
710 hrOpenFailure = S_OK;
711 }
712
713 WcaLog(LOGMSG_VERBOSE, "Configuring Xml File: %ls", pwzFile);
714
715 while (pwz && *pwz)
716 {
717 // If we skip past an element that has additional changes we need to strip them off the stream before
718 // moving on to the next element. Do that now and then restart the outer loop.
719 if (cAdditionalChanges > 0)
720 {
721 while (cAdditionalChanges > 0)
722 {
723 hr = WcaReadStringFromCaData(&pwz, &pwzName);
724 ExitOnFailure(hr, "failed to process CustomActionData");
725 hr = WcaReadStringFromCaData(&pwz, &pwzValue);
726 ExitOnFailure(hr, "failed to process CustomActionData");
727
728 cAdditionalChanges--;
729 }
730 continue;
731 }
732
733 hr = WcaReadIntegerFromCaData(&pwz, (int*) &xa);
734 ExitOnFailure(hr, "failed to process CustomActionData");
735
736 // Break if we need to move on to a different file
737 if (xaOpenFile == xa || xaOpenFilex64 == xa)
738 {
739 break;
740 }
741
742 hr = WcaReadIntegerFromCaData(&pwz, (int*) &xd);
743 ExitOnFailure(hr, "failed to process CustomActionData");
744
745 if (xdPreserve == xd)
746 {
747 fPreserveDate = TRUE;
748 }
749
750 // Get path, name, and value to be written
751 hr = WcaReadStringFromCaData(&pwz, &pwzElementPath);
752 ExitOnFailure(hr, "failed to process CustomActionData");
753 hr = WcaReadStringFromCaData(&pwz, &pwzVerifyPath);
754 ExitOnFailure(hr, "failed to process CustomActionData");
755 hr = WcaReadStringFromCaData(&pwz, &pwzName);
756 ExitOnFailure(hr, "failed to process CustomActionData");
757 hr = WcaReadStringFromCaData(&pwz, &pwzValue);
758 ExitOnFailure(hr, "failed to process CustomActionData");
759 hr = WcaReadIntegerFromCaData(&pwz, &cAdditionalChanges);
760 ExitOnFailure(hr, "failed to process CustomActionData");
761
762 // If we failed to open the file and we're adding something to the file, we've got a problem. Otherwise, just continue on since the file's already gone.
763 if (FAILED(hrOpenFailure))
764 {
765 if (xaCreateElement == xa || xaWriteValue == xa || xaWriteDocument == xa)
766 {
767 MessageExitOnFailure(hr = hrOpenFailure, msierrXmlConfigFailedOpen, "failed to load XML file: %ls", pwzFile);
768 }
769 else
770 {
771 continue;
772 }
773 }
774
775 // Select the node we're about to modify
776 ReleaseNullObject(pixn);
777
778 hr = XmlSelectSingleNode(pixd, pwzElementPath, &pixn);
779
780 // If we failed to find the node that we are going to add to, we've got a problem. Otherwise, just continue since the node's already gone.
781 if (S_FALSE == hr)
782 {
783 if (xaCreateElement == xa || xaWriteValue == xa || xaWriteDocument == xa)
784 {
785 hr = HRESULT_FROM_WIN32(ERROR_OBJECT_NOT_FOUND);
786 }
787 else
788 {
789 hr = S_OK;
790 continue;
791 }
792 }
793
794 MessageExitOnFailure(hr, msierrXmlConfigFailedSelect, "failed to find node: %ls in XML file: %ls", pwzElementPath, pwzFile);
795
796 // Make the modification
797 switch (xa)
798 {
799 case xaWriteValue:
800 if (pwzName && *pwzName)
801 {
802 // We're setting an attribute
803 hr = XmlSetAttribute(pixn, pwzName, pwzValue);
804 ExitOnFailure(hr, "failed to set attribute: %ls to value %ls", pwzName, pwzValue);
805 }
806 else
807 {
808 // We're setting the text of the node
809 hr = XmlSetText(pixn, pwzValue);
810 ExitOnFailure(hr, "failed to set text to: %ls for element %ls. Make sure that XPath points to an element.", pwzValue, pwzElementPath);
811 }
812 break;
813 case xaWriteDocument:
814 if (NULL != pwzVerifyPath && 0 != pwzVerifyPath[0])
815 {
816 hr = XmlSelectSingleNode(pixn, pwzVerifyPath, &pixnVerify);
817 if (S_OK == hr)
818 {
819 // We found the verify path which means we have no further work to do
820 continue;
821 }
822 ExitOnFailure(hr, "failed to query verify path: %ls", pwzVerifyPath);
823 }
824
825 hr = XmlLoadDocumentEx(pwzValue, XML_LOAD_PRESERVE_WHITESPACE, &pixdNew);
826 ExitOnFailure(hr, "Failed to load value as document.");
827
828 hr = pixdNew->get_documentElement(&pixeNew);
829 ExitOnFailure(hr, "Failed to get document element.");
830
831 hr = pixn->appendChild(pixeNew, NULL);
832 ExitOnFailure(hr, "Failed to append document element on to parent element.");
833
834 ReleaseNullObject(pixeNew);
835 ReleaseNullObject(pixdNew);
836 break;
837
838 case xaCreateElement:
839 if (NULL != pwzVerifyPath && 0 != pwzVerifyPath[0])
840 {
841 hr = XmlSelectSingleNode(pixn, pwzVerifyPath, &pixnVerify);
842 if (S_OK == hr)
843 {
844 // We found the verify path which means we have no further work to do
845 continue;
846 }
847 ExitOnFailure(hr, "failed to query verify path: %ls", pwzVerifyPath);
848 }
849
850 hr = XmlCreateChild(pixn, pwzName, &pixnNewNode);
851 ExitOnFailure(hr, "failed to create child element: %ls", pwzName);
852
853 if (pwzValue && *pwzValue)
854 {
855 hr = XmlSetText(pixnNewNode, pwzValue);
856 ExitOnFailure(hr, "failed to set text to: %ls for node: %ls", pwzValue, pwzName);
857 }
858
859 while (cAdditionalChanges > 0)
860 {
861 hr = WcaReadStringFromCaData(&pwz, &pwzName);
862 ExitOnFailure(hr, "failed to process CustomActionData");
863 hr = WcaReadStringFromCaData(&pwz, &pwzValue);
864 ExitOnFailure(hr, "failed to process CustomActionData");
865
866 // Set the additional attribute
867 hr = XmlSetAttribute(pixnNewNode, pwzName, pwzValue);
868 ExitOnFailure(hr, "failed to set attribute: %ls to value %ls", pwzName, pwzValue);
869
870 cAdditionalChanges--;
871 }
872
873 ReleaseNullObject(pixnNewNode);
874 break;
875 case xaDeleteValue:
876 if (pwzName && *pwzName)
877 {
878 // Delete the attribute
879 hr = XmlRemoveAttribute(pixn, pwzName);
880 ExitOnFailure(hr, "failed to remove attribute: %ls", pwzName);
881 }
882 else
883 {
884 // Clear the text value for the node
885 hr = XmlSetText(pixn, L"");
886 ExitOnFailure(hr, "failed to clear text value");
887 }
888 break;
889 case xaDeleteElement:
890 if (NULL != pwzVerifyPath && 0 != pwzVerifyPath[0])
891 {
892 hr = XmlSelectSingleNode(pixn, pwzVerifyPath, &pixnVerify);
893 if (S_OK == hr)
894 {
895 hr = pixn->removeChild(pixnVerify, &pixnRemovedChild);
896 ExitOnFailure(hr, "failed to remove created child element");
897
898 ReleaseNullObject(pixnRemovedChild);
899 }
900 else
901 {
902 WcaLog(LOGMSG_VERBOSE, "Failed to select path %ls for deleting. Skipping...", pwzVerifyPath);
903 hr = S_OK;
904 }
905 }
906 else
907 {
908 // TODO: This requires a VerifyPath to delete an element. Should we support not having one?
909 WcaLog(LOGMSG_VERBOSE, "No VerifyPath specified for delete element of ID: %ls", pwzElementPath);
910 }
911 break;
912 default:
913 ExitOnFailure(hr = E_UNEXPECTED, "Invalid modification specified in custom action data");
914 break;
915 }
916 }
917
918
919 // Now that we've made all of the changes to this file, save it and move on to the next
920 if (S_OK == hrOpenFailure)
921 {
922 if (fPreserveDate)
923 {
924 hr = FileGetTime(pwzFile, NULL, NULL, &ft);
925 ExitOnFailure(hr, "failed to get modified time of file : %ls", pwzFile);
926 }
927
928 int iSaveAttempt = 0;
929
930 do
931 {
932 hr = XmlSaveDocument(pixd, pwzFile);
933 if (FAILED(hr))
934 {
935 id = WcaErrorMessage(msierrXmlConfigFailedSave, hr, INSTALLMESSAGE_ERROR | MB_ABORTRETRYIGNORE, 1, pwzFile);
936 switch (id)
937 {
938 case IDABORT:
939 ExitOnFailure(hr, "Failed to save changes to XML file: %ls", pwzFile);
940 case IDRETRY:
941 hr = S_FALSE; // hit me, baby, one more time
942 break;
943 case IDIGNORE:
944 hr = S_OK; // pretend everything is okay and bail
945 break;
946 case 0: // No UI case, MsiProcessMessage returns 0
947 if (STIERR_SHARING_VIOLATION == hr)
948 {
949 // Only in case of sharing violation do we retry 30 times, once a second.
950 if (iSaveAttempt < 30)
951 {
952 hr = S_FALSE;
953 ++iSaveAttempt;
954 WcaLog(LOGMSG_VERBOSE, "Unable to save changes to XML file: %ls, retry attempt: %x", pwzFile, iSaveAttempt);
955 Sleep(1000);
956 }
957 else
958 {
959 ExitOnFailure(hr, "Failed to save changes to XML file: %ls", pwzFile);
960 }
961 }
962 break;
963 default: // Unknown error
964 ExitOnFailure(hr, "Failed to save changes to XML file: %ls", pwzFile);
965 }
966 }
967 } while (S_FALSE == hr);
968
969 if (fPreserveDate)
970 {
971 hr = FileSetTime(pwzFile, NULL, NULL, &ft);
972 ExitOnFailure(hr, "failed to set modified time of file : %ls", pwzFile);
973 }
974
975#ifndef _WIN64
976 if (fIsFSRedirectDisabled)
977 {
978 fIsFSRedirectDisabled = FALSE;
979 WcaRevertWow64FSRedirection();
980 }
981#endif
982 }
983 }
984
985LExit:
986#ifndef _WIN64
987 // Make sure we revert FS Redirection if necessary before exiting
988 if (fIsFSRedirectDisabled)
989 {
990 fIsFSRedirectDisabled = FALSE;
991 WcaRevertWow64FSRedirection();
992 }
993 WcaFinalizeWow64();
994#endif
995
996 ReleaseStr(pwzCustomActionData);
997 ReleaseStr(pwzData);
998 ReleaseStr(pwzFile);
999 ReleaseStr(pwzElementPath);
1000 ReleaseStr(pwzVerifyPath);
1001 ReleaseStr(pwzName);
1002 ReleaseStr(pwzValue);
1003
1004 ReleaseObject(pixeNew);
1005 ReleaseObject(pixdNew);
1006
1007 ReleaseObject(pixn);
1008 ReleaseObject(pixd);
1009 ReleaseObject(pixnNewNode);
1010 ReleaseObject(pixnRemovedChild);
1011
1012 XmlUninitialize();
1013
1014 if (FAILED(hr))
1015 {
1016 er = ERROR_INSTALL_FAILURE;
1017 }
1018 return WcaFinalize(er);
1019}
1020
1021
1022/******************************************************************
1023 ExecXmlConfigRollback - entry point for XmlConfig rollback Custom Action
1024
1025*******************************************************************/
1026extern "C" UINT __stdcall ExecXmlConfigRollback(
1027 __in MSIHANDLE hInstall
1028 )
1029{
1030// AssertSz(FALSE, "debug ExecXmlConfigRollback");
1031 HRESULT hr = S_OK;
1032 UINT er = ERROR_SUCCESS;
1033
1034 int iIs64Bit;
1035#ifndef _WIN64
1036 BOOL fIs64Bit = FALSE;
1037#endif
1038
1039 LPWSTR pwzCustomActionData = NULL;
1040 LPWSTR pwz = NULL;
1041 LPWSTR pwzFileName = NULL;
1042 LPBYTE pbData = NULL;
1043 DWORD_PTR cbData = 0;
1044
1045 FILETIME ft;
1046
1047 HANDLE hFile = INVALID_HANDLE_VALUE;
1048
1049 // initialize
1050 hr = WcaInitialize(hInstall, "ExecXmlConfigRollback");
1051 ExitOnFailure(hr, "failed to initialize");
1052
1053
1054 hr = WcaGetProperty( L"CustomActionData", &pwzCustomActionData);
1055 ExitOnFailure(hr, "failed to get CustomActionData");
1056
1057 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzCustomActionData);
1058
1059 pwz = pwzCustomActionData;
1060
1061 hr = WcaReadIntegerFromCaData(&pwz, &iIs64Bit);
1062 ExitOnFailure(hr, "failed to read component bitness from custom action data");
1063
1064 hr = WcaReadStringFromCaData(&pwz, &pwzFileName);
1065 ExitOnFailure(hr, "failed to read file name from custom action data");
1066
1067 hr = WcaReadStreamFromCaData(&pwz, &pbData, &cbData);
1068 ExitOnFailure(hr, "failed to read file contents from custom action data");
1069
1070#ifndef _WIN64
1071 fIs64Bit = (BOOL)iIs64Bit;
1072
1073 if (fIs64Bit)
1074 {
1075 hr = WcaInitializeWow64();
1076 if (S_FALSE == hr)
1077 {
1078 hr = TYPE_E_DLLFUNCTIONNOTFOUND;
1079 }
1080 ExitOnFailure(hr, "failed to initialize Wow64 API");
1081
1082 if (!WcaIsWow64Process())
1083 {
1084 hr = E_NOTIMPL;
1085 ExitOnFailure(hr, "Custom action was told to rollback a 64-bit component, but the Wow64 API is unavailable.");
1086 }
1087
1088 hr = WcaDisableWow64FSRedirection();
1089 ExitOnFailure(hr, "Custom action was told to rollback a 64-bit component, but was unable to Disable Filesystem Redirection through the Wow64 API.");
1090 }
1091#endif
1092
1093 hr = FileGetTime(pwzFileName, NULL, NULL, &ft);
1094 ExitOnFailure(hr, "Failed to get modified date of file %ls.", pwzFileName);
1095
1096 // Open the file
1097 hFile = ::CreateFileW(pwzFileName, GENERIC_WRITE, NULL, NULL, TRUNCATE_EXISTING, NULL, NULL);
1098 ExitOnInvalidHandleWithLastError(hFile, hr, "failed to open file: %ls", pwzFileName);
1099
1100 // Write out the old data
1101 hr = FileWriteHandle(hFile, pbData, cbData);
1102 ExitOnFailure(hr, "failed to write to file: %ls", pwzFileName);
1103
1104 ReleaseFile(hFile);
1105
1106 hr = FileSetTime(pwzFileName, NULL, NULL, &ft);
1107 ExitOnFailure(hr, "Failed to set modified date of file %ls.", pwzFileName);
1108
1109LExit:
1110 ReleaseStr(pwzCustomActionData);
1111 ReleaseStr(pwzFileName);
1112
1113 ReleaseFile(hFile);
1114
1115#ifndef _WIN64
1116 if (fIs64Bit)
1117 {
1118 WcaRevertWow64FSRedirection();
1119 WcaFinalizeWow64();
1120 }
1121#endif
1122
1123 ReleaseMem(pbData);
1124
1125 if (FAILED(hr))
1126 {
1127 er = ERROR_INSTALL_FAILURE;
1128 }
1129 return WcaFinalize(er);
1130}
diff --git a/src/ext/Util/ca/XmlFile.cpp b/src/ext/Util/ca/XmlFile.cpp
new file mode 100644
index 00000000..04a4ae98
--- /dev/null
+++ b/src/ext/Util/ca/XmlFile.cpp
@@ -0,0 +1,940 @@
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 XMLFILE_CREATE_ELEMENT 0x00000001
6#define XMLFILE_DELETE_VALUE 0x00000002
7#define XMLFILE_BULKWRITE_VALUE 0x00000004
8
9#define XMLFILE_DONT_UNINSTALL 0x00010000
10#define XMLFILE_PRESERVE_MODIFIED 0x00001000
11#define XMLFILE_USE_XPATH 0x00000100
12
13extern BOOL vfMsxml30;
14
15enum eXmlAction
16{
17 xaOpenFile = 1,
18 xaOpenFilex64,
19 xaWriteValue,
20 xaDeleteValue,
21 xaCreateElement,
22 xaDeleteElement,
23 xaBulkWriteValue,
24};
25
26enum eXmlPreserveDate
27{
28 xdDontPreserve = 0,
29 xdPreserve
30};
31
32enum eXmlSelectionLanguage
33{
34 xsXSLPattern = 0,
35 xsXPath = 1,
36};
37
38LPCWSTR vcsXmlFileQuery =
39 L"SELECT `Wix4XmlFile`.`Wix4XmlFile`, `Wix4XmlFile`.`File`, `Wix4XmlFile`.`ElementPath`, `Wix4XmlFile`.`Name`, `Wix4XmlFile`.`Value`, "
40 L"`Wix4XmlFile`.`Flags`, `Wix4XmlFile`.`Component_`, `Component`.`Attributes` "
41 L"FROM `Wix4XmlFile`,`Component` WHERE `Wix4XmlFile`.`Component_`=`Component`.`Component` ORDER BY `File`, `Sequence`";
42enum eXmlFileQuery { xfqXmlFile = 1, xfqFile, xfqXPath, xfqName, xfqValue, xfqXmlFlags, xfqComponent, xfqCompAttributes };
43
44struct XML_FILE_CHANGE
45{
46 WCHAR wzId[MAX_DARWIN_KEY];
47
48 INSTALLSTATE isInstalled;
49 INSTALLSTATE isAction;
50
51 WCHAR wzFile[MAX_PATH];
52 LPWSTR pwzElementPath;
53 WCHAR wzName[MAX_DARWIN_COLUMN];
54 LPWSTR pwzValue;
55
56 int iXmlFlags;
57 int iCompAttributes;
58
59 XML_FILE_CHANGE* pxfcPrev;
60 XML_FILE_CHANGE* pxfcNext;
61};
62
63//static HRESULT FreeXmlFileChangeList(
64// __in XML_FILE_CHANGE* pxfcList
65// )
66//{
67// HRESULT hr = S_OK;
68//
69// XML_FILE_CHANGE* pxfcDelete;
70// while(pxfcList)
71// {
72// pxfcDelete = pxfcList;
73// pxfcList = pxfcList->pxfcNext;
74//
75// ReleaseStr(pxfcDelete->pwzElementPath);
76// ReleaseStr(pxfcDelete->pwzValue);
77//
78// hr = MemFree(pxfcDelete);
79// ExitOnFailure(hr, "failed to free xml file change list item");
80// }
81//
82//LExit:
83// return hr;
84//}
85
86static HRESULT AddXmlFileChangeToList(
87 __inout XML_FILE_CHANGE** ppxfcHead,
88 __inout XML_FILE_CHANGE** ppxfcTail
89 )
90{
91 Assert(ppxfcHead && ppxfcTail);
92
93 HRESULT hr = S_OK;
94
95 XML_FILE_CHANGE* pxfc = static_cast<XML_FILE_CHANGE*>(MemAlloc(sizeof(XML_FILE_CHANGE), TRUE));
96 ExitOnNull(pxfc, hr, E_OUTOFMEMORY, "failed to allocate memory for new xml file change list element");
97
98 // Add it to the end of the list
99 if (NULL == *ppxfcHead)
100 {
101 *ppxfcHead = pxfc;
102 *ppxfcTail = pxfc;
103 }
104 else
105 {
106 Assert(*ppxfcTail && (*ppxfcTail)->pxfcNext == NULL);
107 (*ppxfcTail)->pxfcNext = pxfc;
108 pxfc->pxfcPrev = *ppxfcTail;
109 *ppxfcTail = pxfc;
110 }
111
112LExit:
113 return hr;
114}
115
116
117static HRESULT ReadXmlFileTable(
118 __inout XML_FILE_CHANGE** ppxfcHead,
119 __inout XML_FILE_CHANGE** ppxfcTail
120 )
121{
122 Assert(ppxfcHead && ppxfcTail);
123
124 HRESULT hr = S_OK;
125 UINT er = ERROR_SUCCESS;
126
127 PMSIHANDLE hView = NULL;
128 PMSIHANDLE hRec = NULL;
129
130 LPWSTR pwzData = NULL;
131
132 // check to see if necessary tables are specified
133 if (S_FALSE == WcaTableExists(L"Wix4XmlFile"))
134 {
135 ExitFunction1(hr = S_FALSE);
136 }
137
138 // loop through all the xml configurations
139 hr = WcaOpenExecuteView(vcsXmlFileQuery, &hView);
140 ExitOnFailure(hr, "failed to open view on Wix4XmlFile table");
141
142 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
143 {
144 hr = AddXmlFileChangeToList(ppxfcHead, ppxfcTail);
145 ExitOnFailure(hr, "failed to add xml file change to list");
146
147 // Get record Id
148 hr = WcaGetRecordString(hRec, xfqXmlFile, &pwzData);
149 ExitOnFailure(hr, "failed to get Wix4XmlFile record Id");
150 hr = StringCchCopyW((*ppxfcTail)->wzId, countof((*ppxfcTail)->wzId), pwzData);
151 ExitOnFailure(hr, "failed to copy Wix4XmlFile record Id");
152
153 // Get component name
154 hr = WcaGetRecordString(hRec, xfqComponent, &pwzData);
155 ExitOnFailure(hr, "failed to get component name for Wix4XmlFile: %ls", (*ppxfcTail)->wzId);
156
157 // Get the component's state
158 er = ::MsiGetComponentStateW(WcaGetInstallHandle(), pwzData, &(*ppxfcTail)->isInstalled, &(*ppxfcTail)->isAction);
159 ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "failed to get install state for Component: %ls", pwzData);
160
161 // Get the xml file
162 hr = WcaGetRecordFormattedString(hRec, xfqFile, &pwzData);
163 ExitOnFailure(hr, "failed to get xml file for Wix4XmlFile: %ls", (*ppxfcTail)->wzId);
164 hr = StringCchCopyW((*ppxfcTail)->wzFile, countof((*ppxfcTail)->wzFile), pwzData);
165 ExitOnFailure(hr, "failed to copy xml file path");
166
167 // Get the Wix4XmlFile table flags
168 hr = WcaGetRecordInteger(hRec, xfqXmlFlags, &(*ppxfcTail)->iXmlFlags);
169 ExitOnFailure(hr, "failed to get Wix4XmlFile flags for Wix4XmlFile: %ls", (*ppxfcTail)->wzId);
170
171 // Get the XPath
172 hr = WcaGetRecordFormattedString(hRec, xfqXPath, &(*ppxfcTail)->pwzElementPath);
173 ExitOnFailure(hr, "failed to get XPath for Wix4XmlFile: %ls", (*ppxfcTail)->wzId);
174
175 // Get the name
176 hr = WcaGetRecordFormattedString(hRec, xfqName, &pwzData);
177 ExitOnFailure(hr, "failed to get Name for Wix4XmlFile: %ls", (*ppxfcTail)->wzId);
178 hr = StringCchCopyW((*ppxfcTail)->wzName, countof((*ppxfcTail)->wzName), pwzData);
179 ExitOnFailure(hr, "failed to copy name of element");
180
181 // Get the value
182 hr = WcaGetRecordFormattedString(hRec, xfqValue, &pwzData);
183 ExitOnFailure(hr, "failed to get Value for Wix4XmlFile: %ls", (*ppxfcTail)->wzId);
184 hr = StrAllocString(&(*ppxfcTail)->pwzValue, pwzData, 0);
185 ExitOnFailure(hr, "failed to allocate buffer for value");
186
187 // Get the component attributes
188 hr = WcaGetRecordInteger(hRec, xfqCompAttributes, &(*ppxfcTail)->iCompAttributes);
189 ExitOnFailure(hr, "failed to get component attributes for Wix4XmlFile: %ls", (*ppxfcTail)->wzId);
190 }
191
192 // if we looped through all records all is well
193 if (E_NOMOREITEMS == hr)
194 hr = S_OK;
195 ExitOnFailure(hr, "failed while looping through all objects to secure");
196
197LExit:
198 ReleaseStr(pwzData);
199
200 return hr;
201}
202
203
204static HRESULT BeginChangeFile(
205 __in LPCWSTR pwzFile,
206 __in XML_FILE_CHANGE* pxfc,
207 __inout LPWSTR* ppwzCustomActionData
208 )
209{
210 Assert(pwzFile && *pwzFile && ppwzCustomActionData);
211
212 HRESULT hr = S_OK;
213 BOOL fIs64Bit = pxfc->iCompAttributes & msidbComponentAttributes64bit;
214 BOOL fUseXPath = pxfc->iXmlFlags & XMLFILE_USE_XPATH;
215 LPBYTE pbData = NULL;
216 SIZE_T cbData = 0;
217
218 LPWSTR pwzRollbackCustomActionData = NULL;
219
220 if (fIs64Bit)
221 {
222 hr = WcaWriteIntegerToCaData((int)xaOpenFilex64, ppwzCustomActionData);
223 ExitOnFailure(hr, "failed to write 64-bit file indicator to custom action data");
224 }
225 else
226 {
227 hr = WcaWriteIntegerToCaData((int)xaOpenFile, ppwzCustomActionData);
228 ExitOnFailure(hr, "failed to write file indicator to custom action data");
229 }
230 if (fUseXPath)
231 {
232 hr = WcaWriteIntegerToCaData((int)xsXPath, ppwzCustomActionData);
233 ExitOnFailure(hr, "failed to write XPath selectionlanguage indicator to custom action data");
234 }
235 else
236 {
237 hr = WcaWriteIntegerToCaData((int)xsXSLPattern, ppwzCustomActionData);
238 ExitOnFailure(hr, "failed to write XSLPattern selectionlanguage indicator to custom action data");
239 }
240 hr = WcaWriteStringToCaData(pwzFile, ppwzCustomActionData);
241 ExitOnFailure(hr, "failed to write file to custom action data: %ls", pwzFile);
242
243 // If the file already exits, then we have to put it back the way it was on failure
244 if (FileExistsEx(pwzFile, NULL))
245 {
246 hr = FileRead(&pbData, &cbData, pwzFile);
247 ExitOnFailure(hr, "failed to read file: %ls", pwzFile);
248
249 // Set up the rollback for this file
250 hr = WcaWriteIntegerToCaData((int)fIs64Bit, &pwzRollbackCustomActionData);
251 ExitOnFailure(hr, "failed to write component bitness to rollback custom action data");
252
253 hr = WcaWriteStringToCaData(pwzFile, &pwzRollbackCustomActionData);
254 ExitOnFailure(hr, "failed to write file name to rollback custom action data: %ls", pwzFile);
255
256 hr = WcaWriteStreamToCaData(pbData, cbData, &pwzRollbackCustomActionData);
257 ExitOnFailure(hr, "failed to write file contents to rollback custom action data.");
258
259 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"ExecXmlFileRollback"), pwzRollbackCustomActionData, COST_XMLFILE);
260 ExitOnFailure(hr, "failed to schedule ExecXmlFileRollback for file: %ls", pwzFile);
261
262 ReleaseStr(pwzRollbackCustomActionData);
263 }
264LExit:
265 ReleaseMem(pbData);
266
267 return hr;
268}
269
270
271static HRESULT WriteChangeData(
272 __in XML_FILE_CHANGE* pxfc,
273 __inout LPWSTR* ppwzCustomActionData
274 )
275{
276 Assert(pxfc && ppwzCustomActionData);
277
278 HRESULT hr = S_OK;
279
280 hr = WcaWriteStringToCaData(pxfc->pwzElementPath, ppwzCustomActionData);
281 ExitOnFailure(hr, "failed to write ElementPath to custom action data: %ls", pxfc->pwzElementPath);
282
283 hr = WcaWriteStringToCaData(pxfc->wzName, ppwzCustomActionData);
284 ExitOnFailure(hr, "failed to write Name to custom action data: %ls", pxfc->wzName);
285
286 hr = WcaWriteStringToCaData(pxfc->pwzValue, ppwzCustomActionData);
287 ExitOnFailure(hr, "failed to write Value to custom action data: %ls", pxfc->pwzValue);
288
289LExit:
290 return hr;
291}
292
293
294/******************************************************************
295 SchedXmlFile - entry point for XmlFile Custom Action
296
297********************************************************************/
298extern "C" UINT __stdcall SchedXmlFile(
299 __in MSIHANDLE hInstall
300 )
301{
302// AssertSz(FALSE, "debug SchedXmlFile");
303
304 HRESULT hr = S_OK;
305 UINT er = ERROR_SUCCESS;
306
307 LPWSTR pwzCurrentFile = NULL;
308 BOOL fCurrentFileChanged = FALSE;
309 BOOL fCurrentUseXPath = FALSE;
310
311 PMSIHANDLE hView = NULL;
312 PMSIHANDLE hRec = NULL;
313
314 XML_FILE_CHANGE* pxfcHead = NULL;
315 XML_FILE_CHANGE* pxfcTail = NULL;
316 XML_FILE_CHANGE* pxfc = NULL;
317 XML_FILE_CHANGE* pxfcUninstall = NULL;
318
319 LPWSTR pwzCustomActionData = NULL;
320
321 DWORD cFiles = 0;
322
323 // initialize
324 hr = WcaInitialize(hInstall, "SchedXmlFile");
325 ExitOnFailure(hr, "failed to initialize");
326
327 hr = ReadXmlFileTable(&pxfcHead, &pxfcTail);
328 if (S_FALSE == hr)
329 {
330 WcaLog(LOGMSG_VERBOSE, "Skipping SchedXmlFile because Wix4XmlFile table not present");
331 ExitFunction1(hr = S_OK);
332 }
333
334 MessageExitOnFailure(hr, msierrXmlFileFailedRead, "failed to read Wix4XmlFile table");
335
336 // loop through all the xml configurations
337 for (pxfc = pxfcHead; pxfc; pxfc = pxfc->pxfcNext)
338 {
339 // If this is the first file, a different file, the last file, or the SelectionLanguage property changes...
340 if (NULL == pwzCurrentFile || 0 != lstrcmpW(pwzCurrentFile, pxfc->wzFile) || NULL == pxfc->pxfcNext || fCurrentUseXPath != ((XMLFILE_USE_XPATH & pxfc->iXmlFlags)))
341 {
342 // If this isn't the first file
343 if (NULL != pwzCurrentFile)
344 {
345 // Do the uninstall work for the current file by walking backwards through the list (so the sequence is reversed)
346 for (pxfcUninstall = ((NULL != pxfc->pxfcNext) ? pxfc->pxfcPrev : pxfc); pxfcUninstall && 0 == lstrcmpW(pwzCurrentFile, pxfcUninstall->wzFile) && fCurrentUseXPath == ((XMLFILE_USE_XPATH & pxfcUninstall->iXmlFlags)); pxfcUninstall = pxfcUninstall->pxfcPrev)
347 {
348 // If it's being uninstalled
349 if (WcaIsUninstalling(pxfcUninstall->isInstalled, pxfcUninstall->isAction))
350 {
351 // Uninstall the change
352 if (!(XMLFILE_DONT_UNINSTALL & pxfcUninstall->iXmlFlags))
353 {
354 if (!fCurrentFileChanged)
355 {
356 hr = BeginChangeFile(pwzCurrentFile, pxfcUninstall, &pwzCustomActionData);
357 ExitOnFailure(hr, "failed to begin file change for file: %ls", pwzCurrentFile);
358
359 fCurrentFileChanged = TRUE;
360 ++cFiles;
361 }
362 if (XMLFILE_CREATE_ELEMENT & pxfcUninstall->iXmlFlags)
363 {
364 hr = WcaWriteIntegerToCaData((int)xaDeleteElement, &pwzCustomActionData);
365 ExitOnFailure(hr, "failed to write delete element action indicator to custom action data");
366 }
367 else
368 {
369 hr = WcaWriteIntegerToCaData((int)xaDeleteValue, &pwzCustomActionData);
370 ExitOnFailure(hr, "failed to write delete value action indicator to custom action data");
371 }
372
373 if (XMLFILE_PRESERVE_MODIFIED & pxfc->iXmlFlags)
374 {
375 hr = WcaWriteIntegerToCaData((int)xdPreserve, &pwzCustomActionData);
376 ExitOnFailure(hr, "failed to write Preserve Date indicator to custom action data");
377 }
378 else
379 {
380 hr = WcaWriteIntegerToCaData((int)xdDontPreserve, &pwzCustomActionData);
381 ExitOnFailure(hr, "failed to write Don't Preserve Date indicator to custom action data");
382 }
383
384 hr = WriteChangeData(pxfcUninstall, &pwzCustomActionData);
385 ExitOnFailure(hr, "failed to write uninstall change data");
386 }
387 }
388 }
389 }
390
391 // Remember the file we're currently working on
392 hr = StrAllocString(&pwzCurrentFile, pxfc->wzFile, 0);
393 ExitOnFailure(hr, "failed to copy file name");
394 fCurrentUseXPath = (XMLFILE_USE_XPATH & pxfc->iXmlFlags);
395
396 // We haven't changed the current file yet
397 fCurrentFileChanged = FALSE;
398 }
399
400 // If it's being installed
401 if (WcaIsInstalling(pxfc->isInstalled, pxfc->isAction))
402 {
403 if (!fCurrentFileChanged)
404 {
405 hr = BeginChangeFile(pwzCurrentFile, pxfc, &pwzCustomActionData);
406 ExitOnFailure(hr, "failed to begin file change for file: %ls", pwzCurrentFile);
407 fCurrentFileChanged = TRUE;
408 ++cFiles;
409 }
410
411 // Install the change
412 if (XMLFILE_CREATE_ELEMENT & pxfc->iXmlFlags)
413 {
414 hr = WcaWriteIntegerToCaData((int)xaCreateElement, &pwzCustomActionData);
415 ExitOnFailure(hr, "failed to write create element action indicator to custom action data");
416 }
417 else if (XMLFILE_DELETE_VALUE & pxfc->iXmlFlags)
418 {
419 hr = WcaWriteIntegerToCaData((int)xaDeleteValue, &pwzCustomActionData);
420 ExitOnFailure(hr, "failed to write delete value action indicator to custom action data");
421 }
422 else if (XMLFILE_BULKWRITE_VALUE & pxfc->iXmlFlags)
423 {
424 hr = WcaWriteIntegerToCaData((int)xaBulkWriteValue, &pwzCustomActionData);
425 ExitOnFailure(hr, "failed to write builkwrite value action indicator to custom action data");
426 }
427 else
428 {
429 hr = WcaWriteIntegerToCaData((int)xaWriteValue, &pwzCustomActionData);
430 ExitOnFailure(hr, "failed to write file indicator to custom action data");
431 }
432
433 if (XMLFILE_PRESERVE_MODIFIED & pxfc->iXmlFlags)
434 {
435 hr = WcaWriteIntegerToCaData((int)xdPreserve, &pwzCustomActionData);
436 ExitOnFailure(hr, "failed to write Preserve Date indicator to custom action data");
437 }
438 else
439 {
440 hr = WcaWriteIntegerToCaData((int)xdDontPreserve, &pwzCustomActionData);
441 ExitOnFailure(hr, "failed to write Don't Preserve Date indicator to custom action data");
442 }
443
444 hr = WriteChangeData(pxfc, &pwzCustomActionData);
445 ExitOnFailure(hr, "failed to write change data");
446 }
447 }
448
449 // If we looped through all records all is well
450 if (E_NOMOREITEMS == hr)
451 hr = S_OK;
452 ExitOnFailure(hr, "failed while looping through all objects to secure");
453
454 // Schedule the custom action and add to progress bar
455 if (pwzCustomActionData && *pwzCustomActionData)
456 {
457 Assert(0 < cFiles);
458
459 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"ExecXmlFile"), pwzCustomActionData, cFiles * COST_XMLFILE);
460 ExitOnFailure(hr, "failed to schedule ExecXmlFile action");
461 }
462
463LExit:
464 ReleaseStr(pwzCurrentFile);
465 ReleaseStr(pwzCustomActionData);
466
467 return WcaFinalize(FAILED(hr) ? ERROR_INSTALL_FAILURE : er);
468}
469
470
471/******************************************************************
472 ExecXmlFile - entry point for XmlFile Custom Action
473
474*******************************************************************/
475extern "C" UINT __stdcall ExecXmlFile(
476 __in MSIHANDLE hInstall
477 )
478{
479// AssertSz(FALSE, "debug ExecXmlFile");
480 HRESULT hr = S_OK;
481 HRESULT hrOpenFailure = S_OK;
482 UINT er = ERROR_SUCCESS;
483
484 BOOL fIsFSRedirectDisabled = FALSE;
485 BOOL fPreserveDate = FALSE;
486
487 int id = IDRETRY;
488
489 LPWSTR pwzCustomActionData = NULL;
490 LPWSTR pwzData = NULL;
491 LPWSTR pwzFile = NULL;
492 LPWSTR pwzXPath = NULL;
493 LPWSTR pwzName = NULL;
494 LPWSTR pwzValue = NULL;
495 LPWSTR pwz = NULL;
496
497 IXMLDOMDocument* pixd = NULL;
498 IXMLDOMNode* pixn = NULL;
499 IXMLDOMNode* pixnNewNode = NULL;
500 IXMLDOMNodeList* pixNodes = NULL;
501 IXMLDOMDocument2 *pixdDocument2 = NULL;
502
503 FILETIME ft;
504
505 BSTR bstrProperty = ::SysAllocString(L"SelectionLanguage");
506 ExitOnNull(bstrProperty, hr, E_OUTOFMEMORY, "failed SysAllocString");
507 VARIANT varValue;
508 ::VariantInit(&varValue);
509 varValue.vt = VT_BSTR;
510 varValue.bstrVal = ::SysAllocString(L"XPath");
511 ExitOnNull(varValue.bstrVal, hr, E_OUTOFMEMORY, "failed SysAllocString");
512 eXmlAction xa;
513 eXmlPreserveDate xd;
514 eXmlSelectionLanguage xl;
515
516 // initialize
517 hr = WcaInitialize(hInstall, "ExecXmlFile");
518 ExitOnFailure(hr, "failed to initialize");
519
520 hr = XmlInitialize();
521 ExitOnFailure(hr, "failed to initialize xml utilities");
522
523 hr = WcaGetProperty( L"CustomActionData", &pwzCustomActionData);
524 ExitOnFailure(hr, "failed to get CustomActionData");
525
526 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzCustomActionData);
527
528 pwz = pwzCustomActionData;
529
530 hr = WcaReadIntegerFromCaData(&pwz, (int*) &xa);
531 ExitOnFailure(hr, "failed to process CustomActionData");
532
533#ifndef _WIN64
534 // Initialize the Wow64 API - store the result in fWow64APIPresent
535 // If it fails, this doesn't warrant an error yet, because we only need the Wow64 API in some cases
536 WcaInitializeWow64();
537 BOOL fIsWow64Process = WcaIsWow64Process();
538#endif
539
540 if (xaOpenFile != xa && xaOpenFilex64 != xa)
541 ExitOnFailure(hr = E_INVALIDARG, "invalid custom action data");
542
543 // loop through all the passed in data
544 while (pwz && *pwz)
545 {
546 hr = WcaReadIntegerFromCaData(&pwz, (int*) &xl);
547 ExitOnFailure(hr, "failed to process CustomActionData");
548
549 hr = WcaReadStringFromCaData(&pwz, &pwzFile);
550 ExitOnFailure(hr, "failed to read file name from custom action data");
551
552 // Default to not preserve the modified date
553 fPreserveDate = FALSE;
554
555 // Open the file
556 ReleaseNullObject(pixd);
557
558 if (xaOpenFilex64 == xa)
559 {
560#ifndef _WIN64
561 if (!fIsWow64Process)
562 {
563 hr = E_NOTIMPL;
564 ExitOnFailure(hr, "Custom action was told to act on a 64-bit component, but the custom action process is not running in WOW.");
565 }
566
567 hr = WcaDisableWow64FSRedirection();
568 ExitOnFailure(hr, "Custom action was told to act on a 64-bit component, but was unable to disable filesystem redirection through the Wow64 API.");
569
570 fIsFSRedirectDisabled = TRUE;
571#endif
572 }
573
574 hr = XmlLoadDocumentFromFileEx(pwzFile, XML_LOAD_PRESERVE_WHITESPACE, &pixd);
575 if (FAILED(hr))
576 {
577 // Ignore the return code for now. If they try to add something, we'll fail the install. If all they do is remove stuff then it doesn't matter.
578 hrOpenFailure = hr;
579 hr = S_OK;
580 }
581 else
582 {
583 hrOpenFailure = S_OK;
584 }
585 WcaLog(LOGMSG_VERBOSE, "Configuring Xml File: %ls", pwzFile);
586
587 if (xsXPath == xl)
588 {
589 if (vfMsxml30)
590 {
591 // If we failed to open the file, don't fail immediately; just skip setting the selection language, and we'll fail later if appropriate
592 if (SUCCEEDED(hrOpenFailure))
593 {
594 hr = pixd->QueryInterface(XmlUtil_IID_IXMLDOMDocument2, (void**)&pixdDocument2);
595 ExitOnFailure(hr, "failed in querying IXMLDOMDocument2 interface");
596 hr = pixdDocument2->setProperty(bstrProperty, varValue);
597 ExitOnFailure(hr, "failed in setting SelectionLanguage");
598 }
599 }
600 else
601 {
602 ExitOnFailure(hr = E_NOTIMPL, "Error: current MSXML version does not support xpath query.");
603 }
604 }
605
606 while (pwz && *pwz)
607 {
608 hr = WcaReadIntegerFromCaData(&pwz, (int*) &xa);
609 ExitOnFailure(hr, "failed to process CustomActionData");
610
611 // Break if we need to move on to a different file
612 if (xaOpenFile == xa || xaOpenFilex64 == xa)
613 break;
614
615 hr = WcaReadIntegerFromCaData(&pwz, (int*) &xd);
616 ExitOnFailure(hr, "failed to process CustomActionData");
617
618 if (xdPreserve == xd)
619 {
620 fPreserveDate = TRUE;
621 }
622
623 // Get path, name, and value to be written
624 hr = WcaReadStringFromCaData(&pwz, &pwzXPath);
625 ExitOnFailure(hr, "failed to process CustomActionData");
626 hr = WcaReadStringFromCaData(&pwz, &pwzName);
627 ExitOnFailure(hr, "failed to process CustomActionData");
628 hr = WcaReadStringFromCaData(&pwz, &pwzValue);
629 ExitOnFailure(hr, "failed to process CustomActionData");
630
631 // If we failed to open the file and we're adding something to the file, we've got a problem. Otherwise, just continue on since the file's already gone.
632 if (FAILED(hrOpenFailure))
633 {
634 if (xaCreateElement == xa || xaWriteValue == xa || xaBulkWriteValue == xa)
635 {
636 MessageExitOnFailure(hr = hrOpenFailure, msierrXmlFileFailedOpen, "failed to load XML file: %ls", pwzFile);
637 }
638 else
639 {
640 continue;
641 }
642 }
643
644 // Select the node we're about to modify
645 ReleaseNullObject(pixn);
646
647 if (xaBulkWriteValue == xa)
648 {
649 hr = XmlSelectNodes(pixd, pwzXPath, &pixNodes);
650 if (S_FALSE == hr)
651 {
652 hr = HRESULT_FROM_WIN32(ERROR_OBJECT_NOT_FOUND);
653 }
654
655 MessageExitOnFailure(hr, msierrXmlFileFailedSelect, "failed to find any nodes: %ls in XML file: %ls", pwzXPath, pwzFile);
656 for (;;)
657 {
658 pixNodes->nextNode(&pixn);
659 if (NULL == pixn)
660 break;
661
662 if (pwzName && *pwzName)
663 {
664 // We're setting an attribute
665 hr = XmlSetAttribute(pixn, pwzName, pwzValue);
666 ExitOnFailure(hr, "failed to set attribute: %ls to value %ls", pwzName, pwzValue);
667 }
668 else
669 {
670 // We're setting the text of the node
671 hr = XmlSetText(pixn, pwzValue);
672 ExitOnFailure(hr, "failed to set text to: %ls for element %ls. Make sure that XPath points to an element.", pwzValue, pwzXPath);
673 }
674 ReleaseNullObject(pixn);
675 }
676 }
677 else
678 {
679 hr = XmlSelectSingleNode(pixd, pwzXPath, &pixn);
680 if (S_FALSE == hr)
681 hr = HRESULT_FROM_WIN32(ERROR_OBJECT_NOT_FOUND);
682 MessageExitOnFailure(hr, msierrXmlFileFailedSelect, "failed to find node: %ls in XML file: %ls", pwzXPath, pwzFile);
683
684 // Make the modification
685 if (xaWriteValue == xa)
686 {
687 if (pwzName && *pwzName)
688 {
689 // We're setting an attribute
690 hr = XmlSetAttribute(pixn, pwzName, pwzValue);
691 ExitOnFailure(hr, "failed to set attribute: %ls to value %ls", pwzName, pwzValue);
692 }
693 else
694 {
695 // We're setting the text of the node
696 hr = XmlSetText(pixn, pwzValue);
697 ExitOnFailure(hr, "failed to set text to: %ls for element %ls. Make sure that XPath points to an element.", pwzValue, pwzXPath);
698 }
699 }
700 else if (xaCreateElement == xa)
701 {
702 hr = XmlCreateChild(pixn, pwzName, &pixnNewNode);
703 ExitOnFailure(hr, "failed to create child element: %ls", pwzName);
704
705 if (pwzValue && *pwzValue)
706 {
707 hr = XmlSetText(pixnNewNode, pwzValue);
708 ExitOnFailure(hr, "failed to set text to: %ls for node: %ls", pwzValue, pwzName);
709 }
710
711 ReleaseNullObject(pixnNewNode);
712 }
713 else if (xaDeleteValue == xa)
714 {
715 if (pwzName && *pwzName)
716 {
717 // Delete the attribute
718 hr = XmlRemoveAttribute(pixn, pwzName);
719 ExitOnFailure(hr, "failed to remove attribute: %ls", pwzName);
720 }
721 else
722 {
723 // Clear the text value for the node
724 hr = XmlSetText(pixn, L"");
725 ExitOnFailure(hr, "failed to clear text value");
726 }
727 }
728 else if (xaDeleteElement == xa)
729 {
730 // TODO: This may be a little heavy handed
731 hr = XmlRemoveChildren(pixn, pwzName);
732 ExitOnFailure(hr, "failed to delete child node: %ls", pwzName);
733 }
734 else
735 {
736 ExitOnFailure(hr = E_UNEXPECTED, "Invalid modification specified in custom action data");
737 }
738 }
739 }
740
741 // Now that we've made all of the changes to this file, save it and move on to the next
742 if (S_OK == hrOpenFailure)
743 {
744 if (fPreserveDate)
745 {
746 hr = FileGetTime(pwzFile, NULL, NULL, &ft);
747 ExitOnFailure(hr, "failed to get modified time of file : %ls", pwzFile);
748 }
749
750 int iSaveAttempt = 0;
751
752 do
753 {
754 hr = XmlSaveDocument(pixd, pwzFile);
755 if (FAILED(hr))
756 {
757 id = WcaErrorMessage(msierrXmlConfigFailedSave, hr, INSTALLMESSAGE_ERROR | MB_ABORTRETRYIGNORE, 1, pwzFile);
758 switch (id)
759 {
760 case IDABORT:
761 ExitOnFailure(hr, "Failed to save changes to XML file: %ls", pwzFile);
762 case IDRETRY:
763 hr = S_FALSE; // hit me, baby, one more time
764 break;
765 case IDIGNORE:
766 hr = S_OK; // pretend everything is okay and bail
767 break;
768 case 0: // No UI case, MsiProcessMessage returns 0
769 if (STIERR_SHARING_VIOLATION == hr)
770 {
771 // Only in case of sharing violation do we retry 30 times, once a second.
772 if (iSaveAttempt < 30)
773 {
774 hr = S_FALSE;
775 ++iSaveAttempt;
776 WcaLog(LOGMSG_VERBOSE, "Unable to save changes to XML file: %ls, retry attempt: %x", pwzFile, iSaveAttempt);
777 Sleep(1000);
778 }
779 else
780 {
781 ExitOnFailure(hr, "Failed to save changes to XML file: %ls", pwzFile);
782 }
783 }
784 break;
785 default: // Unknown error
786 ExitOnFailure(hr, "Failed to save changes to XML file: %ls", pwzFile);
787 }
788 }
789 } while (S_FALSE == hr);
790
791 if (fPreserveDate)
792 {
793 hr = FileSetTime(pwzFile, NULL, NULL, &ft);
794 ExitOnFailure(hr, "failed to set modified time of file : %ls", pwzFile);
795 }
796
797 if (fIsFSRedirectDisabled)
798 {
799 fIsFSRedirectDisabled = FALSE;
800 WcaRevertWow64FSRedirection();
801 }
802 }
803 }
804
805LExit:
806 // Make sure we revert FS Redirection if necessary before exiting
807 if (fIsFSRedirectDisabled)
808 {
809 fIsFSRedirectDisabled = FALSE;
810 WcaRevertWow64FSRedirection();
811 }
812#ifndef _WIN64
813 WcaFinalizeWow64();
814#endif
815
816 ReleaseStr(pwzCustomActionData);
817 ReleaseStr(pwzData);
818 ReleaseStr(pwzFile);
819 ReleaseStr(pwzXPath);
820 ReleaseStr(pwzName);
821 ReleaseStr(pwzValue);
822 ReleaseBSTR(bstrProperty);
823 ReleaseVariant(varValue);
824
825 ReleaseObject(pixdDocument2);
826 ReleaseObject(pixn);
827 ReleaseObject(pixd);
828 ReleaseObject(pixnNewNode);
829 ReleaseObject(pixNodes);
830
831 XmlUninitialize();
832
833 return WcaFinalize(FAILED(hr) ? ERROR_INSTALL_FAILURE : er);
834}
835
836
837/******************************************************************
838 ExecXmlFileRollback - entry point for XmlFile rollback Custom Action
839
840*******************************************************************/
841extern "C" UINT __stdcall ExecXmlFileRollback(
842 __in MSIHANDLE hInstall
843 )
844{
845// AssertSz(FALSE, "debug ExecXmlFileRollback");
846 HRESULT hr = S_OK;
847 UINT er = ERROR_SUCCESS;
848
849 int iIs64Bit;
850 BOOL fIs64Bit = FALSE;
851
852 LPWSTR pwzCustomActionData = NULL;
853 LPWSTR pwz = NULL;
854 LPWSTR pwzFileName = NULL;
855 LPBYTE pbData = NULL;
856 DWORD_PTR cbData = 0;
857
858 FILETIME ft;
859
860 HANDLE hFile = INVALID_HANDLE_VALUE;
861
862 // initialize
863 hr = WcaInitialize(hInstall, "ExecXmlFileRollback");
864 ExitOnFailure(hr, "failed to initialize");
865
866
867 hr = WcaGetProperty( L"CustomActionData", &pwzCustomActionData);
868 ExitOnFailure(hr, "failed to get CustomActionData");
869
870 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzCustomActionData);
871
872 pwz = pwzCustomActionData;
873
874 hr = WcaReadIntegerFromCaData(&pwz, &iIs64Bit);
875 ExitOnFailure(hr, "failed to read component bitness from custom action data");
876
877 hr = WcaReadStringFromCaData(&pwz, &pwzFileName);
878 ExitOnFailure(hr, "failed to read file name from custom action data");
879
880 hr = WcaReadStreamFromCaData(&pwz, &pbData, &cbData);
881 ExitOnFailure(hr, "failed to read file contents from custom action data");
882
883#ifndef _WIN64
884 fIs64Bit = (BOOL)iIs64Bit;
885
886 if (fIs64Bit)
887 {
888 hr = WcaInitializeWow64();
889 if (S_FALSE == hr)
890 {
891 hr = TYPE_E_DLLFUNCTIONNOTFOUND;
892 }
893 ExitOnFailure(hr, "failed to initialize Wow64 API");
894
895 if (!WcaIsWow64Process())
896 {
897 hr = E_NOTIMPL;
898 ExitOnFailure(hr, "Custom action was told to rollback a 64-bit component, but the custom action process is not running in WOW.");
899 }
900
901 hr = WcaDisableWow64FSRedirection();
902 ExitOnFailure(hr, "Custom action was told to rollback a 64-bit component, but was unable to Disable Filesystem Redirection through the Wow64 API.");
903 }
904#endif
905
906 // Always preserve the modified date on rollback
907 hr = FileGetTime(pwzFileName, NULL, NULL, &ft);
908 ExitOnFailure(hr, "Failed to get modified date of file %ls.", pwzFileName);
909
910 // Open the file
911 hFile = ::CreateFileW(pwzFileName, GENERIC_WRITE, NULL, NULL, TRUNCATE_EXISTING, NULL, NULL);
912 ExitOnInvalidHandleWithLastError(hFile, hr, "failed to open file: %ls", pwzFileName);
913
914 // Write out the old data
915 hr = FileWriteHandle(hFile, pbData, cbData);
916 ExitOnFailure(hr, "failed to write to file: %ls", pwzFileName);
917
918 ReleaseFile(hFile);
919
920 // Always preserve the modified date on rollback
921 hr = FileSetTime(pwzFileName, NULL, NULL, &ft);
922 ExitOnFailure(hr, "Failed to set modified date of file %ls.", pwzFileName);
923
924LExit:
925 ReleaseStr(pwzCustomActionData);
926 ReleaseStr(pwzFileName);
927
928 ReleaseFile(hFile);
929
930 if (fIs64Bit)
931 {
932 WcaRevertWow64FSRedirection();
933 WcaFinalizeWow64();
934 }
935
936 ReleaseMem(pbData);
937
938 return WcaFinalize(FAILED(hr) ? ERROR_INSTALL_FAILURE : er);
939}
940
diff --git a/src/ext/Util/ca/caDecor.h b/src/ext/Util/ca/caDecor.h
new file mode 100644
index 00000000..da274650
--- /dev/null
+++ b/src/ext/Util/ca/caDecor.h
@@ -0,0 +1,13 @@
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(_M_ARM64)
6#define CUSTOM_ACTION_DECORATION(f) L"Wix4" f L"_A64"
7#elif defined(_M_AMD64)
8#define CUSTOM_ACTION_DECORATION(f) L"Wix4" f L"_X64"
9#elif defined(_M_ARM)
10#define CUSTOM_ACTION_DECORATION(f) L"Wix4" f L"_ARM"
11#else
12#define CUSTOM_ACTION_DECORATION(f) L"Wix4" f L"_X86"
13#endif
diff --git a/src/ext/Util/ca/cost.h b/src/ext/Util/ca/cost.h
new file mode 100644
index 00000000..6507e85d
--- /dev/null
+++ b/src/ext/Util/ca/cost.h
@@ -0,0 +1,9 @@
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
5const UINT COST_SECUREOBJECT = 1000;
6const UINT COST_SERVICECONFIG = 1000;
7const UINT COST_XMLFILE = 1000;
8const UINT COST_CLOSEAPP = 500;
9const UINT COST_INTERNETSHORTCUT = 2000;
diff --git a/src/ext/Util/ca/dllmain.cpp b/src/ext/Util/ca/dllmain.cpp
new file mode 100644
index 00000000..35ae6d1c
--- /dev/null
+++ b/src/ext/Util/ca/dllmain.cpp
@@ -0,0 +1,26 @@
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/********************************************************************
6DllMain - standard entry point for all WiX custom actions
7
8********************************************************************/
9extern "C" BOOL WINAPI DllMain(
10 IN HINSTANCE hInst,
11 IN ULONG ulReason,
12 IN LPVOID)
13{
14 switch(ulReason)
15 {
16 case DLL_PROCESS_ATTACH:
17 WcaGlobalInitialize(hInst);
18 break;
19
20 case DLL_PROCESS_DETACH:
21 WcaGlobalFinalize();
22 break;
23 }
24
25 return TRUE;
26}
diff --git a/src/ext/Util/ca/exitearlywithsuccess.cpp b/src/ext/Util/ca/exitearlywithsuccess.cpp
new file mode 100644
index 00000000..00828329
--- /dev/null
+++ b/src/ext/Util/ca/exitearlywithsuccess.cpp
@@ -0,0 +1,27 @@
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/******************************************************************
7WixExitEarlyWithSuccess - entry point for WixExitEarlyWithSuccess
8 custom action which does nothing except return exit code
9 ERROR_NO_MORE_ITEMS. The Windows Installer documentation at
10 http://msdn.microsoft.com/library/aa368072.aspx indicates that
11 this exit code is not treated as an error. This will cause a
12 calling application to receive a successful return code if
13 this custom action executes. This can be useful for backwards
14 compatibility when an application redistributes an MSI and
15 a future major upgrade is released for that MSI. It should be
16 conditioned on a property set by an entry in the Upgrade table
17 of the MSI that detects newer major upgrades of the same MSI
18 already installed on the system. It should be scheduled after
19 the FindRelatedProducts action so that the property will be
20 set if appropriate.
21********************************************************************/
22extern "C" UINT __stdcall WixExitEarlyWithSuccess(
23 __in MSIHANDLE /*hInstall*/
24 )
25{
26 return ERROR_NO_MORE_ITEMS;
27}
diff --git a/src/ext/Util/ca/netshortcuts.cpp b/src/ext/Util/ca/netshortcuts.cpp
new file mode 100644
index 00000000..06826264
--- /dev/null
+++ b/src/ext/Util/ca/netshortcuts.cpp
@@ -0,0 +1,437 @@
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
5LPCWSTR vcsShortcutsQuery =
6 L"SELECT `Component_`, `Directory_`, `Name`, `Target`, `Attributes`, `IconFile`, `IconIndex` "
7 L"FROM `Wix4InternetShortcut`";
8enum eShortcutsQuery { esqComponent = 1, esqDirectory, esqFilename, esqTarget, esqAttributes, esqIconFile, esqIconIndex };
9enum eShortcutsAttributes { esaLink = 0, esaURL = 1 };
10
11/******************************************************************
12 WixSchedInternetShortcuts - entry point
13
14********************************************************************/
15extern "C" UINT __stdcall WixSchedInternetShortcuts(
16 __in MSIHANDLE hInstall
17 )
18{
19 HRESULT hr = S_OK;
20 UINT er = ERROR_SUCCESS;
21
22 UINT uiCost = 0;
23
24 PMSIHANDLE hView = NULL;
25 PMSIHANDLE hRec = NULL;
26
27 MSIHANDLE hCreateFolderTable = NULL;
28 MSIHANDLE hCreateFolderColumns = NULL;
29
30 LPWSTR pwzCustomActionData = NULL;
31 LPWSTR pwzComponent = NULL;
32 LPWSTR pwzDirectory = NULL;
33 LPWSTR pwzFilename = NULL;
34 LPWSTR pwzTarget = NULL;
35 LPWSTR pwzShortcutPath = NULL;
36 int iAttr = 0;
37 LPWSTR pwzIconFile = NULL;
38 int iIconIndex = 0;
39 IUniformResourceLocatorW* piURL = NULL;
40 IShellLinkW* piShellLink = NULL;
41 BOOL fInitializedCom = FALSE;
42
43 hr = WcaInitialize(hInstall, "WixSchedInternetShortcuts");
44 ExitOnFailure(hr, "failed to initialize WixSchedInternetShortcuts.");
45
46 // anything to do?
47 if (S_OK != WcaTableExists(L"Wix4InternetShortcut"))
48 {
49 WcaLog(LOGMSG_STANDARD, "Wix4InternetShortcut table doesn't exist, so there are no Internet shortcuts to process");
50 goto LExit;
51 }
52
53 // check to see if we can create a shortcut - Server Core and others may not have a shell registered.
54 hr = ::CoInitialize(NULL);
55 ExitOnFailure(hr, "failed to initialize COM");
56 fInitializedCom = TRUE;
57
58 hr = ::CoCreateInstance(CLSID_InternetShortcut, NULL, CLSCTX_ALL, IID_IUniformResourceLocatorW, (void**)&piURL);
59 if (S_OK != hr)
60 {
61 WcaLog(LOGMSG_STANDARD, "failed to create an instance of IUniformResourceLocatorW, skipping shortcut creation");
62 ExitFunction1(hr = S_OK);
63 }
64
65 hr = ::CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_ALL, IID_IShellLinkW, (void**)&piShellLink);
66 if (S_OK != hr)
67 {
68 WcaLog(LOGMSG_STANDARD, "failed to create an instance of IShellLinkW, skipping shortcut creation");
69 ExitFunction1(hr = S_OK);
70 }
71
72 // query and loop through all the shortcuts
73 hr = WcaOpenExecuteView(vcsShortcutsQuery, &hView);
74 ExitOnFailure(hr, "failed to open view on Wix4InternetShortcut table");
75
76 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
77 {
78 // read column values
79 hr = WcaGetRecordString(hRec, esqComponent, &pwzComponent);
80 ExitOnFailure(hr, "failed to get shortcut component");
81 hr = WcaGetRecordString(hRec, esqDirectory, &pwzDirectory);
82 ExitOnFailure(hr, "failed to get shortcut directory");
83 hr = WcaGetRecordString(hRec, esqFilename, &pwzFilename);
84 ExitOnFailure(hr, "failed to get shortcut filename");
85 hr = WcaGetRecordFormattedString(hRec, esqTarget, &pwzTarget);
86 ExitOnFailure(hr, "failed to get shortcut target");
87 hr = WcaGetRecordInteger(hRec, esqAttributes, &iAttr);
88 ExitOnFailure(hr, "failed to get shortcut attributes");
89 hr = WcaGetRecordFormattedString(hRec, esqIconFile, &pwzIconFile);
90 ExitOnFailure(hr, "failed to get shortcut icon file");
91 hr = WcaGetRecordInteger(hRec, esqIconIndex, &iIconIndex);
92 ExitOnFailure(hr, "failed to get shortcut icon index");
93
94 // skip processing this Wix4InternetShortcut row if the component isn't being configured
95 WCA_TODO todo = WcaGetComponentToDo(pwzComponent);
96 if (WCA_TODO_UNKNOWN == todo)
97 {
98 WcaLog(LOGMSG_VERBOSE, "Skipping shortcut for null-action component '%ls'", pwzComponent);
99 continue;
100 }
101
102 // we need to create the directory where the shortcut is supposed to live; rather
103 // than doing so in our deferred custom action, use the CreateFolder table to have MSI
104 // make (and remove) them on our behalf (including the correct cleanup of parent directories).
105 MSIDBERROR dbError = MSIDBERROR_NOERROR;
106 WcaLog(LOGMSG_STANDARD, "Adding folder '%ls', component '%ls' to the CreateFolder table", pwzDirectory, pwzComponent);
107 hr = WcaAddTempRecord(&hCreateFolderTable, &hCreateFolderColumns, L"CreateFolder", &dbError, 0, 2, pwzDirectory, pwzComponent);
108 if (MSIDBERROR_DUPLICATEKEY == dbError)
109 {
110 WcaLog(LOGMSG_STANDARD, "Folder '%ls' already exists in the CreateFolder table; the above error is harmless", pwzDirectory);
111 hr = S_OK;
112 }
113 ExitOnFailure(hr, "Couldn't add temporary CreateFolder row");
114
115 // only if we're installing/reinstalling do we need to schedule the deferred CA
116 // (uninstallation is handled via permanent RemoveFile rows and temporary CreateFolder rows)
117 if (WCA_TODO_INSTALL == todo || WCA_TODO_REINSTALL == todo)
118 {
119 // turn the Directory_ id into a path
120 hr = WcaGetTargetPath(pwzDirectory, &pwzShortcutPath);
121 ExitOnFailure(hr, "failed to allocate string for shortcut directory");
122
123 // append the shortcut filename
124 hr = StrAllocConcat(&pwzShortcutPath, pwzFilename, 0);
125 ExitOnFailure(hr, "failed to allocate string for shortcut filename");
126
127 // write the shortcut path and target to custom action data for deferred CAs
128 hr = WcaWriteStringToCaData(pwzShortcutPath, &pwzCustomActionData);
129 ExitOnFailure(hr, "failed to write shortcut path to custom action data");
130 hr = WcaWriteStringToCaData(pwzTarget, &pwzCustomActionData);
131 ExitOnFailure(hr, "failed to write shortcut target to custom action data");
132 hr = WcaWriteIntegerToCaData(iAttr, &pwzCustomActionData);
133 ExitOnFailure(hr, "failed to write shortcut attributes to custom action data");
134 hr = WcaWriteStringToCaData(pwzIconFile, &pwzCustomActionData);
135 ExitOnFailure(hr, "failed to write icon file to custom action data");
136 hr = WcaWriteIntegerToCaData(iIconIndex, &pwzCustomActionData);
137 ExitOnFailure(hr, "failed to write icon index to custom action data");
138
139 uiCost += COST_INTERNETSHORTCUT;
140 }
141 }
142
143 if (E_NOMOREITEMS == hr)
144 {
145 hr = S_OK;
146 }
147 ExitOnFailure(hr, "Failure occured while processing Wix4InternetShortcut table");
148
149 // if we have any shortcuts to install
150 if (pwzCustomActionData && *pwzCustomActionData)
151 {
152 // add cost to progress bar
153 hr = WcaProgressMessage(uiCost, TRUE);
154 ExitOnFailure(hr, "failed to extend progress bar for InternetShortcuts");
155
156 // provide custom action data to deferred and rollback CAs
157 hr = WcaSetProperty(CUSTOM_ACTION_DECORATION(L"RollbackInternetShortcuts"), pwzCustomActionData);
158 ExitOnFailure(hr, "failed to set WixRollbackInternetShortcuts rollback custom action data");
159 hr = WcaSetProperty(CUSTOM_ACTION_DECORATION(L"CreateInternetShortcuts"), pwzCustomActionData);
160 ExitOnFailure(hr, "failed to set WixCreateInternetShortcuts custom action data");
161 }
162
163LExit:
164 if (hCreateFolderTable)
165 {
166 ::MsiCloseHandle(hCreateFolderTable);
167 }
168
169 if (hCreateFolderColumns)
170 {
171 ::MsiCloseHandle(hCreateFolderColumns);
172 }
173
174 ReleaseStr(pwzCustomActionData);
175 ReleaseStr(pwzComponent);
176 ReleaseStr(pwzDirectory);
177 ReleaseStr(pwzFilename);
178 ReleaseStr(pwzTarget);
179 ReleaseStr(pwzShortcutPath);
180 ReleaseObject(piShellLink);
181 ReleaseObject(piURL);
182
183 if (fInitializedCom)
184 {
185 ::CoUninitialize();
186 }
187
188 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
189 return WcaFinalize(er);
190}
191
192
193
194/******************************************************************
195 CreateUrl - Creates a shortcut via IUniformResourceLocatorW
196
197*******************************************************************/
198static HRESULT CreateUrl(
199 __in_z LPCWSTR wzTarget,
200 __in_z LPCWSTR wzShortcutPath,
201 __in_z_opt LPCWSTR wzIconPath,
202 __in int iconIndex
203)
204{
205 HRESULT hr = S_OK;
206 IUniformResourceLocatorW* piURL = NULL;
207 IPersistFile* piPersistFile = NULL;
208 IPropertySetStorage* piProperties = NULL;
209 IPropertyStorage* piStorage = NULL;
210
211 // create an internet shortcut object
212 WcaLog(LOGMSG_STANDARD, "Creating IUniformResourceLocatorW shortcut '%ls' target '%ls'", wzShortcutPath, wzTarget);
213 hr = ::CoCreateInstance(CLSID_InternetShortcut, NULL, CLSCTX_ALL, IID_IUniformResourceLocatorW, (void**)&piURL);
214 ExitOnFailure(hr, "failed to create an instance of IUniformResourceLocatorW");
215
216 // set shortcut target
217 hr = piURL->SetURL(wzTarget, 0);
218 ExitOnFailure(hr, "failed to set shortcut '%ls' target '%ls'", wzShortcutPath, wzTarget);
219
220 if (wzIconPath)
221 {
222 WcaLog(LOGMSG_STANDARD, "Adding icon '%ls' index '%d'", wzIconPath, iconIndex);
223
224 hr = piURL->QueryInterface(IID_IPropertySetStorage, (void **)&piProperties);
225 ExitOnFailure(hr, "failed to get IPropertySetStorage for shortcut '%ls'", wzShortcutPath);
226
227 hr = piProperties->Open(FMTID_Intshcut, STGM_WRITE, &piStorage);
228 ExitOnFailure(hr, "failed to open storage for shortcut '%ls'", wzShortcutPath);
229
230 PROPSPEC ppids[2] = { {PRSPEC_PROPID, PID_IS_ICONINDEX}, {PRSPEC_PROPID, PID_IS_ICONFILE} };
231 PROPVARIANT ppvar[2];
232
233 PropVariantInit(ppvar);
234 PropVariantInit(ppvar + 1);
235
236 ppvar[0].vt = VT_I4;
237 ppvar[0].lVal = iconIndex;
238 ppvar[1].vt = VT_LPWSTR;
239 ppvar[1].pwszVal = const_cast<LPWSTR>(wzIconPath);
240
241 hr = piStorage->WriteMultiple(2, ppids, ppvar, 0);
242 ExitOnFailure(hr, "failed to write icon storage for shortcut '%ls'", wzShortcutPath);
243
244 hr = piStorage->Commit(STGC_DEFAULT);
245 ExitOnFailure(hr, "failed to commit icon storage for shortcut '%ls'", wzShortcutPath);
246 }
247
248 // get an IPersistFile and save the shortcut
249 hr = piURL->QueryInterface(IID_IPersistFile, (void**)&piPersistFile);
250 ExitOnFailure(hr, "failed to get IPersistFile for shortcut '%ls'", wzShortcutPath);
251
252 hr = piPersistFile->Save(wzShortcutPath, TRUE);
253 ExitOnFailure(hr, "failed to save shortcut '%ls'", wzShortcutPath);
254
255LExit:
256 ReleaseObject(piPersistFile);
257 ReleaseObject(piURL);
258 ReleaseObject(piStorage);
259 ReleaseObject(piProperties);
260
261 return hr;
262}
263
264/******************************************************************
265 CreateLink - Creates a shortcut via IShellLinkW
266
267*******************************************************************/
268static HRESULT CreateLink(
269 __in_z LPCWSTR wzTarget,
270 __in_z LPCWSTR wzShortcutPath,
271 __in_z_opt LPCWSTR wzIconPath,
272 __in int iconIndex
273)
274{
275 HRESULT hr = S_OK;
276 IShellLinkW* piShellLink = NULL;
277 IPersistFile* piPersistFile = NULL;
278
279 // create an internet shortcut object
280 WcaLog(LOGMSG_STANDARD, "Creating IShellLinkW shortcut '%ls' target '%ls'", wzShortcutPath, wzTarget);
281 hr = ::CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_ALL, IID_IShellLinkW, (void**)&piShellLink);
282 ExitOnFailure(hr, "failed to create an instance of IShellLinkW");
283
284 // set shortcut target
285 hr = piShellLink->SetPath(wzTarget);
286 ExitOnFailure(hr, "failed to set shortcut '%ls' target '%ls'", wzShortcutPath, wzTarget);
287
288 if (wzIconPath)
289 {
290 WcaLog(LOGMSG_STANDARD, "Adding icon '%ls' index '%d'", wzIconPath, iconIndex);
291 hr = piShellLink->SetIconLocation(wzIconPath, iconIndex);
292 ExitOnFailure(hr, "failed to set icon for shortcut '%ls'", wzShortcutPath);
293 }
294
295 // get an IPersistFile and save the shortcut
296 hr = piShellLink->QueryInterface(IID_IPersistFile, (void**)&piPersistFile);
297 ExitOnFailure(hr, "failed to get IPersistFile for shortcut '%ls'", wzShortcutPath);
298
299 hr = piPersistFile->Save(wzShortcutPath, TRUE);
300 ExitOnFailure(hr, "failed to save shortcut '%ls'", wzShortcutPath);
301
302LExit:
303 ReleaseObject(piPersistFile);
304 ReleaseObject(piShellLink);
305
306 return hr;
307}
308
309
310
311/******************************************************************
312 WixCreateInternetShortcuts - entry point for Internet shortcuts
313 custom action
314*******************************************************************/
315extern "C" UINT __stdcall WixCreateInternetShortcuts(
316 __in MSIHANDLE hInstall
317 )
318{
319 HRESULT hr = S_OK;
320 UINT er = ERROR_SUCCESS;
321
322 LPWSTR pwz = NULL;
323 LPWSTR pwzCustomActionData = NULL;
324 LPWSTR pwzTarget = NULL;
325 LPWSTR pwzShortcutPath = NULL;
326 LPWSTR pwzIconPath = NULL;
327 BOOL fInitializedCom = FALSE;
328 int iAttr = 0;
329 int iIconIndex = 0;
330
331 // initialize
332 hr = WcaInitialize(hInstall, "WixCreateInternetShortcuts");
333 ExitOnFailure(hr, "failed to initialize WixCreateInternetShortcuts");
334
335 hr = ::CoInitialize(NULL);
336 ExitOnFailure(hr, "failed to initialize COM");
337 fInitializedCom = TRUE;
338
339 // extract the custom action data
340 hr = WcaGetProperty(L"CustomActionData", &pwzCustomActionData);
341 ExitOnFailure(hr, "failed to get CustomActionData");
342
343 // loop through all the custom action data
344 pwz = pwzCustomActionData;
345 while (pwz && *pwz)
346 {
347 hr = WcaReadStringFromCaData(&pwz, &pwzShortcutPath);
348 ExitOnFailure(hr, "failed to read shortcut path from custom action data");
349 hr = WcaReadStringFromCaData(&pwz, &pwzTarget);
350 ExitOnFailure(hr, "failed to read shortcut target from custom action data");
351 hr = WcaReadIntegerFromCaData(&pwz, &iAttr);
352 ExitOnFailure(hr, "failed to read shortcut attributes from custom action data");
353 hr = WcaReadStringFromCaData(&pwz, &pwzIconPath);
354 ExitOnFailure(hr, "failed to read shortcut icon path from custom action data");
355 hr = WcaReadIntegerFromCaData(&pwz, &iIconIndex);
356 ExitOnFailure(hr, "failed to read shortcut icon index from custom action data");
357
358 if ((iAttr & esaURL) == esaURL)
359 {
360 hr = CreateUrl(pwzTarget, pwzShortcutPath, pwzIconPath, iIconIndex);
361 }
362 else
363 {
364 hr = CreateLink(pwzTarget, pwzShortcutPath, pwzIconPath, iIconIndex);
365 }
366 ExitOnFailure(hr, "failed to create Internet shortcut");
367
368 // tick the progress bar
369 hr = WcaProgressMessage(COST_INTERNETSHORTCUT, FALSE);
370 ExitOnFailure(hr, "failed to tick progress bar for shortcut: %ls", pwzShortcutPath);
371 }
372
373LExit:
374 ReleaseStr(pwzCustomActionData);
375 ReleaseStr(pwzTarget);
376 ReleaseStr(pwzShortcutPath);
377
378 if (fInitializedCom)
379 {
380 ::CoUninitialize();
381 }
382
383 er = FAILED(hr) ? ERROR_INSTALL_FAILURE : er;
384 return WcaFinalize(er);
385}
386
387
388
389/******************************************************************
390 WixRollbackInternetShortcuts - entry point for Internet shortcuts
391 custom action (rollback)
392*******************************************************************/
393extern "C" UINT __stdcall WixRollbackInternetShortcuts(
394 __in MSIHANDLE hInstall
395 )
396{
397 HRESULT hr = S_OK;
398 UINT er = ERROR_SUCCESS;
399
400 LPWSTR pwz = NULL;
401 LPWSTR pwzCustomActionData = NULL;
402 LPWSTR pwzShortcutPath = NULL;
403 int iAttr = 0;
404
405 // initialize
406 hr = WcaInitialize(hInstall, "WixRemoveInternetShortcuts");
407 ExitOnFailure(hr, "failed to initialize WixRemoveInternetShortcuts");
408
409 hr = WcaGetProperty(L"CustomActionData", &pwzCustomActionData);
410 ExitOnFailure(hr, "failed to get CustomActionData");
411
412 // loop through all the custom action data
413 pwz = pwzCustomActionData;
414 while (pwz && *pwz)
415 {
416 // extract the custom action data we're interested in
417 hr = WcaReadStringFromCaData(&pwz, &pwzShortcutPath);
418 ExitOnFailure(hr, "failed to read shortcut path from custom action data for rollback");
419
420 // delete file
421 hr = FileEnsureDelete(pwzShortcutPath);
422 ExitOnFailure(hr, "failed to delete file '%ls'", pwzShortcutPath);
423
424 // skip over the shortcut target and attributes
425 hr = WcaReadStringFromCaData(&pwz, &pwzShortcutPath);
426 ExitOnFailure(hr, "failed to skip shortcut target from custom action data for rollback");
427 hr = WcaReadIntegerFromCaData(&pwz, &iAttr);
428 ExitOnFailure(hr, "failed to read shortcut attributes from custom action data");
429 }
430
431LExit:
432 ReleaseStr(pwzCustomActionData);
433 ReleaseStr(pwzShortcutPath);
434
435 er = FAILED(hr) ? ERROR_INSTALL_FAILURE : er;
436 return WcaFinalize(er);
437}
diff --git a/src/ext/Util/ca/precomp.h b/src/ext/Util/ca/precomp.h
new file mode 100644
index 00000000..c5d6afe5
--- /dev/null
+++ b/src/ext/Util/ca/precomp.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 _WIN32_MSI < 150
6#define _WIN32_MSI 150
7#endif
8
9#include <windows.h>
10#include <msiquery.h>
11#include <msidefs.h>
12#include <stierr.h>
13
14#include <strsafe.h>
15
16#include <msxml2.h>
17#include <Iads.h>
18#include <activeds.h>
19#include <lm.h> // NetApi32.lib
20#include <Ntsecapi.h>
21#include <Dsgetdc.h>
22#include <shlobj.h>
23#include <intshcut.h>
24
25#define MAXUINT USHRT_MAX
26
27#include "wcautil.h"
28#include "wcawow64.h"
29#include "wcawrapquery.h"
30#include "aclutil.h"
31#include "dirutil.h"
32#include "fileutil.h"
33#include "memutil.h"
34#include "osutil.h"
35#include "pathutil.h"
36#include "procutil.h"
37#include "shelutil.h"
38#include "strutil.h"
39#include "sczutil.h"
40#include "rmutil.h"
41#include "userutil.h"
42#include "xmlutil.h"
43#include "wiutil.h"
44
45#include "CustomMsiErrors.h"
46
47#include "sca.h"
48#include "scacost.h"
49#include "cost.h"
50#include "scauser.h"
51#include "scasmb.h"
52#include "scasmbexec.h"
53
54#include "caDecor.h"
diff --git a/src/ext/Util/ca/qtexecca.cpp b/src/ext/Util/ca/qtexecca.cpp
new file mode 100644
index 00000000..ddcc812f
--- /dev/null
+++ b/src/ext/Util/ca/qtexecca.cpp
@@ -0,0 +1,316 @@
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 OUTPUT_BUFFER 1024
6
7// These old "CA" prefix names are deprecated, and intended to go away in wix 4.0, only staying now for compatibility reasons
8const LPCWSTR CAQUIET_TIMEOUT_PROPERTY = L"QtExecCmdTimeout";
9const LPCWSTR CAQUIET_ARGUMENTS_PROPERTY = L"QtExecCmdLine";
10const LPCWSTR CAQUIET64_ARGUMENTS_PROPERTY = L"QtExec64CmdLine";
11// end deprecated section
12
13// WixCA name quiet commandline argument properties
14const LPCWSTR WIX_QUIET_ARGUMENTS_PROPERTY = L"WixQuietExecCmdLine";
15const LPCWSTR WIX_QUIET64_ARGUMENTS_PROPERTY = L"WixQuietExec64CmdLine";
16
17// WixCA quiet timeout properties
18const LPCWSTR WIX_QUIET_TIMEOUT_PROPERTY = L"WixQuietExecCmdTimeout";
19const LPCWSTR WIX_QUIET64_TIMEOUT_PROPERTY = L"WixQuietExec64CmdTimeout";
20
21// WixCA silent commandline argument properties
22const LPCWSTR WIX_SILENT_ARGUMENTS_PROPERTY = L"WixSilentExecCmdLine";
23const LPCWSTR WIX_SILENT64_ARGUMENTS_PROPERTY = L"WixSilentExec64CmdLine";
24
25// WixCA silent timeout properties
26const LPCWSTR WIX_SILENT_TIMEOUT_PROPERTY = L"WixSilentExecCmdTimeout";
27const LPCWSTR WIX_SILENT64_TIMEOUT_PROPERTY = L"WixSilentExec64CmdTimeout";
28
29HRESULT BuildCommandLine(
30 __in LPCWSTR wzProperty,
31 __out LPWSTR *ppwzCommand
32 )
33{
34 Assert(ppwzCommand);
35
36 HRESULT hr = S_OK;
37 BOOL fScheduled = ::MsiGetMode(WcaGetInstallHandle(), MSIRUNMODE_SCHEDULED);
38 BOOL fRollback = ::MsiGetMode(WcaGetInstallHandle(), MSIRUNMODE_ROLLBACK);
39 BOOL fCommit = ::MsiGetMode(WcaGetInstallHandle(), MSIRUNMODE_COMMIT);
40
41 if (fScheduled || fRollback || fCommit)
42 {
43 if (WcaIsPropertySet("CustomActionData"))
44 {
45 hr = WcaGetProperty( L"CustomActionData", ppwzCommand);
46 ExitOnFailure(hr, "Failed to get CustomActionData");
47 }
48 }
49 else if (WcaIsUnicodePropertySet(wzProperty))
50 {
51 hr = WcaGetFormattedProperty(wzProperty, ppwzCommand);
52 ExitOnFailure(hr, "Failed to get %ls", wzProperty);
53 hr = WcaSetProperty(wzProperty, L""); // clear out the property now that we've read it
54 ExitOnFailure(hr, "Failed to set %ls", wzProperty);
55 }
56
57 if (!*ppwzCommand)
58 {
59 ExitOnFailure(hr = E_INVALIDARG, "Failed to get command line data");
60 }
61
62 if (L'"' != **ppwzCommand)
63 {
64 WcaLog(LOGMSG_STANDARD, "Command string must begin with quoted application name.");
65 ExitOnFailure(hr = E_INVALIDARG, "invalid command line property value");
66 }
67
68LExit:
69 return hr;
70}
71
72#define ONEMINUTE 60000
73
74DWORD GetTimeout(LPCWSTR wzPropertyName)
75{
76 DWORD dwTimeout = ONEMINUTE;
77 HRESULT hr = S_OK;
78
79 LPWSTR pwzData = NULL;
80
81 if (WcaIsUnicodePropertySet(wzPropertyName))
82 {
83 hr = WcaGetProperty(wzPropertyName, &pwzData);
84 ExitOnFailure(hr, "Failed to get %ls", wzPropertyName);
85
86 if ((dwTimeout = (DWORD)_wtoi(pwzData)) == 0)
87 {
88 dwTimeout = ONEMINUTE;
89 }
90 }
91
92LExit:
93 ReleaseStr(pwzData);
94
95 return dwTimeout;
96
97}
98
99HRESULT ExecCommon(
100 __in LPCWSTR wzArgumentsProperty,
101 __in LPCWSTR wzTimeoutProperty,
102 __in BOOL fLogCommand,
103 __in BOOL fLogOutput
104 )
105{
106 HRESULT hr = S_OK;
107 LPWSTR pwzCommand = NULL;
108 DWORD dwTimeout = 0;
109
110 hr = BuildCommandLine(wzArgumentsProperty, &pwzCommand);
111 ExitOnFailure(hr, "Failed to get Command Line");
112
113 dwTimeout = GetTimeout(wzTimeoutProperty);
114
115 hr = QuietExec(pwzCommand, dwTimeout, fLogCommand, fLogOutput);
116 ExitOnFailure(hr, "QuietExec Failed");
117
118LExit:
119 ReleaseStr(pwzCommand);
120
121 return hr;
122}
123
124HRESULT ExecCommon64(
125 __in LPCWSTR wzArgumentsProperty,
126 __in LPCWSTR wzTimeoutProperty,
127 __in BOOL fLogCommand,
128 __in BOOL fLogOutput
129 )
130{
131 HRESULT hr = S_OK;
132 LPWSTR pwzCommand = NULL;
133 DWORD dwTimeout = 0;
134#ifndef _WIN64
135 BOOL fIsWow64Initialized = FALSE;
136 BOOL fRedirected = FALSE;
137
138 hr = WcaInitializeWow64();
139 if (S_FALSE == hr)
140 {
141 hr = TYPE_E_DLLFUNCTIONNOTFOUND;
142 }
143 ExitOnFailure(hr, "Failed to intialize WOW64.");
144 fIsWow64Initialized = TRUE;
145
146 hr = WcaDisableWow64FSRedirection();
147 ExitOnFailure(hr, "Failed to enable filesystem redirection.");
148 fRedirected = TRUE;
149#endif
150
151 hr = BuildCommandLine(wzArgumentsProperty, &pwzCommand);
152 ExitOnFailure(hr, "Failed to get Command Line");
153
154 dwTimeout = GetTimeout(wzTimeoutProperty);
155
156 hr = QuietExec(pwzCommand, dwTimeout, fLogCommand, fLogOutput);
157 ExitOnFailure(hr, "QuietExec64 Failed");
158
159LExit:
160 ReleaseStr(pwzCommand);
161
162#ifndef _WIN64
163 if (fRedirected)
164 {
165 WcaRevertWow64FSRedirection();
166 }
167
168 if (fIsWow64Initialized)
169 {
170 WcaFinalizeWow64();
171 }
172#endif
173
174 return hr;
175}
176
177// These two custom actions are deprecated, and should go away in wix v4.0. WixQuietExec replaces this one,
178// and is not intended to have any difference in behavior apart from CA name and property names.
179extern "C" UINT __stdcall CAQuietExec(
180 __in MSIHANDLE hInstall
181 )
182{
183 Assert(hInstall);
184 HRESULT hr = S_OK;
185 UINT er = ERROR_SUCCESS;
186
187 hr = WcaInitialize(hInstall, "CAQuietExec");
188 ExitOnFailure(hr, "Failed to initialize");
189
190 hr = ExecCommon(CAQUIET_ARGUMENTS_PROPERTY, CAQUIET_TIMEOUT_PROPERTY, TRUE, TRUE);
191 ExitOnFailure(hr, "Failed in ExecCommon method");
192
193LExit:
194 if (FAILED(hr))
195 {
196 er = ERROR_INSTALL_FAILURE;
197 }
198
199 return WcaFinalize(er);
200}
201
202// 2nd deprecated custom action name, superseded by WixQuietExec64
203extern "C" UINT __stdcall CAQuietExec64(
204 __in MSIHANDLE hInstall
205 )
206{
207 Assert(hInstall);
208 HRESULT hr = S_OK;
209 UINT er = ERROR_SUCCESS;
210
211 hr = WcaInitialize(hInstall, "CAQuietExec64");
212 ExitOnFailure(hr, "Failed to initialize");
213
214 hr = ExecCommon64(CAQUIET64_ARGUMENTS_PROPERTY, CAQUIET_TIMEOUT_PROPERTY, TRUE, TRUE);
215 ExitOnFailure(hr, "Failed in ExecCommon64 method");
216
217LExit:
218 if (FAILED(hr))
219 {
220 er = ERROR_INSTALL_FAILURE;
221 }
222
223 return WcaFinalize(er);
224}
225
226extern "C" UINT __stdcall WixQuietExec(
227 __in MSIHANDLE hInstall
228 )
229{
230 Assert(hInstall);
231 HRESULT hr = S_OK;
232 UINT er = ERROR_SUCCESS;
233
234 hr = WcaInitialize(hInstall, "WixQuietExec");
235 ExitOnFailure(hr, "Failed to initialize");
236
237 hr = ExecCommon(WIX_QUIET_ARGUMENTS_PROPERTY, WIX_QUIET_TIMEOUT_PROPERTY, TRUE, TRUE);
238 ExitOnFailure(hr, "Failed in ExecCommon method");
239
240LExit:
241 if (FAILED(hr))
242 {
243 er = ERROR_INSTALL_FAILURE;
244 }
245
246 return WcaFinalize(er);
247}
248
249extern "C" UINT __stdcall WixQuietExec64(
250 __in MSIHANDLE hInstall
251 )
252{
253 Assert(hInstall);
254 HRESULT hr = S_OK;
255 UINT er = ERROR_SUCCESS;
256
257 hr = WcaInitialize(hInstall, "WixQuietExec64");
258 ExitOnFailure(hr, "Failed to initialize");
259
260 hr = ExecCommon64(WIX_QUIET64_ARGUMENTS_PROPERTY, WIX_QUIET64_TIMEOUT_PROPERTY, TRUE, TRUE);
261 ExitOnFailure(hr, "Failed in ExecCommon method");
262
263LExit:
264 if (FAILED(hr))
265 {
266 er = ERROR_INSTALL_FAILURE;
267 }
268
269 return WcaFinalize(er);
270}
271
272extern "C" UINT __stdcall WixSilentExec(
273 __in MSIHANDLE hInstall
274 )
275{
276 Assert(hInstall);
277 HRESULT hr = S_OK;
278 UINT er = ERROR_SUCCESS;
279
280 hr = WcaInitialize(hInstall, "WixSilentExec");
281 ExitOnFailure(hr, "Failed to initialize");
282
283 hr = ExecCommon(WIX_SILENT_ARGUMENTS_PROPERTY, WIX_SILENT_TIMEOUT_PROPERTY, FALSE, FALSE);
284 ExitOnFailure(hr, "Failed in ExecCommon method");
285
286LExit:
287 if (FAILED(hr))
288 {
289 er = ERROR_INSTALL_FAILURE;
290 }
291
292 return WcaFinalize(er);
293}
294
295extern "C" UINT __stdcall WixSilentExec64(
296 __in MSIHANDLE hInstall
297 )
298{
299 Assert(hInstall);
300 HRESULT hr = S_OK;
301 UINT er = ERROR_SUCCESS;
302
303 hr = WcaInitialize(hInstall, "WixSilentExec64");
304 ExitOnFailure(hr, "Failed to initialize");
305
306 hr = ExecCommon64(WIX_SILENT64_ARGUMENTS_PROPERTY, WIX_SILENT64_TIMEOUT_PROPERTY, FALSE, FALSE);
307 ExitOnFailure(hr, "Failed in ExecCommon method");
308
309LExit:
310 if (FAILED(hr))
311 {
312 er = ERROR_INSTALL_FAILURE;
313 }
314
315 return WcaFinalize(er);
316}
diff --git a/src/ext/Util/ca/sca.h b/src/ext/Util/ca/sca.h
new file mode 100644
index 00000000..599122ff
--- /dev/null
+++ b/src/ext/Util/ca/sca.h
@@ -0,0 +1,19 @@
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// user creation attributes definitions
5enum SCAU_ATTRIBUTES
6{
7 SCAU_DONT_EXPIRE_PASSWRD = 0x00000001,
8 SCAU_PASSWD_CANT_CHANGE = 0x00000002,
9 SCAU_PASSWD_CHANGE_REQD_ON_LOGIN = 0x00000004,
10 SCAU_DISABLE_ACCOUNT = 0x00000008,
11 SCAU_FAIL_IF_EXISTS = 0x00000010,
12 SCAU_UPDATE_IF_EXISTS = 0x00000020,
13 SCAU_ALLOW_LOGON_AS_SERVICE = 0x00000040,
14 SCAU_ALLOW_LOGON_AS_BATCH = 0x00000080,
15
16 SCAU_DONT_REMOVE_ON_UNINSTALL = 0x00000100,
17 SCAU_DONT_CREATE_USER = 0x00000200,
18 SCAU_NON_VITAL = 0x00000400,
19}; \ No newline at end of file
diff --git a/src/ext/Util/ca/scacost.h b/src/ext/Util/ca/scacost.h
new file mode 100644
index 00000000..5b215035
--- /dev/null
+++ b/src/ext/Util/ca/scacost.h
@@ -0,0 +1,18 @@
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
5const UINT COST_PERFMON_REGISTER = 1000;
6const UINT COST_PERFMON_UNREGISTER = 1000;
7
8const UINT COST_SMB_CREATESMB = 10000;
9const UINT COST_SMB_DROPSMB = 5000;
10const UINT COST_USER_ADD = 10000;
11const UINT COST_USER_DELETE = 10000;
12
13const UINT COST_PERFMONMANIFEST_REGISTER = 1000;
14const UINT COST_PERFMONMANIFEST_UNREGISTER = 1000;
15
16const UINT COST_EVENTMANIFEST_REGISTER = 1000;
17const UINT COST_EVENTMANIFEST_UNREGISTER = 1000;
18
diff --git a/src/ext/Util/ca/scaexec.cpp b/src/ext/Util/ca/scaexec.cpp
new file mode 100644
index 00000000..5845c1b4
--- /dev/null
+++ b/src/ext/Util/ca/scaexec.cpp
@@ -0,0 +1,1082 @@
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 * CreateSmb - CUSTOM ACTION ENTRY POINT for creating fileshares
8 *
9 * Input: deferred CustomActionData -
10 * wzFsKey\twzShareDesc\twzFullPath\tfIntegratedAuth\twzUserName\tnPermissions\twzUserName\tnPermissions...
11 *
12 * ****************************************************************/
13extern "C" UINT __stdcall CreateSmb(MSIHANDLE hInstall)
14{
15//AssertSz(0, "debug CreateSmb");
16 UINT er = ERROR_SUCCESS;
17 HRESULT hr = S_OK;
18
19 LPWSTR pwzData = NULL;
20 LPWSTR pwz = NULL;
21 LPWSTR pwzFsKey = NULL;
22 LPWSTR pwzShareDesc = NULL;
23 LPWSTR pwzDirectory = NULL;
24 int iAccessMode = 0;
25 DWORD nExPermissions = 0;
26 BOOL fIntegratedAuth;
27 LPWSTR pwzExUser = NULL;
28 SCA_SMBP ssp = {0};
29 DWORD dwExUserPerms = 0;
30 DWORD dwCounter = 0;
31 SCA_SMBP_USER_PERMS* pUserPermsList = NULL;
32
33 hr = WcaInitialize(hInstall, "CreateSmb");
34 ExitOnFailure(hr, "failed to initialize");
35
36 hr = WcaGetProperty( L"CustomActionData", &pwzData);
37 ExitOnFailure(hr, "failed to get CustomActionData");
38
39 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData);
40
41 pwz = pwzData;
42 hr = WcaReadStringFromCaData(&pwz, &pwzFsKey); // share name
43 ExitOnFailure(hr, "failed to read share name");
44 hr = WcaReadStringFromCaData(&pwz, &pwzShareDesc); // share description
45 ExitOnFailure(hr, "failed to read share name");
46 hr = WcaReadStringFromCaData(&pwz, &pwzDirectory); // full path to share
47 ExitOnFailure(hr, "failed to read share name");
48 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int *>(&fIntegratedAuth));
49 ExitOnFailure(hr, "failed to read integrated authentication");
50
51 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int *>(&dwExUserPerms));
52 ExitOnFailure(hr, "failed to read count of permissions to set");
53 if(dwExUserPerms > 0)
54 {
55 pUserPermsList = static_cast<SCA_SMBP_USER_PERMS*>(MemAlloc(sizeof(SCA_SMBP_USER_PERMS)*dwExUserPerms, TRUE));
56 ExitOnNull(pUserPermsList, hr, E_OUTOFMEMORY, "failed to allocate memory for permissions structure");
57
58 //Pull out all of the ExUserPerm strings
59 for (dwCounter = 0; dwCounter < dwExUserPerms; ++dwCounter)
60 {
61 hr = WcaReadStringFromCaData(&pwz, &pwzExUser); // user account
62 ExitOnFailure(hr, "failed to read user account");
63 pUserPermsList[dwCounter].wzUser = pwzExUser;
64 pwzExUser = NULL;
65
66 hr = WcaReadIntegerFromCaData(&pwz, &iAccessMode);
67 ExitOnFailure(hr, "failed to read access mode");
68 pUserPermsList[dwCounter].accessMode = (ACCESS_MODE)iAccessMode;
69 iAccessMode = 0;
70
71 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int *>(&nExPermissions));
72 ExitOnFailure(hr, "failed to read count of permissions");
73 pUserPermsList[dwCounter].nPermissions = nExPermissions;
74 nExPermissions = 0;
75 }
76 }
77
78 ssp.wzKey = pwzFsKey;
79 ssp.wzDescription = pwzShareDesc;
80 ssp.wzDirectory = pwzDirectory;
81 ssp.fUseIntegratedAuth = fIntegratedAuth;
82 ssp.dwUserPermissionCount = dwExUserPerms;
83 ssp.pUserPerms = pUserPermsList;
84
85 hr = ScaEnsureSmbExists(&ssp);
86 MessageExitOnFailure(hr, msierrSMBFailedCreate, "failed to create share: '%ls'", pwzFsKey);
87
88 hr = WcaProgressMessage(COST_SMB_CREATESMB, FALSE);
89
90LExit:
91 ReleaseStr(pwzFsKey);
92 ReleaseStr(pwzShareDesc);
93 ReleaseStr(pwzDirectory);
94 ReleaseStr(pwzData);
95
96 if (pUserPermsList)
97 {
98 MemFree(pUserPermsList);
99 }
100
101 if (FAILED(hr))
102 {
103 er = ERROR_INSTALL_FAILURE;
104 }
105 return WcaFinalize(er);
106}
107
108
109
110/********************************************************************
111 DropSmb - CUSTOM ACTION ENTRY POINT for creating fileshares
112
113 Input: deferred CustomActionData - wzFsKey\twzShareDesc\twzFullPath\tnPermissions\tfIntegratedAuth\twzUserName\twzPassword
114
115 * ****************************************************************/
116extern "C" UINT __stdcall DropSmb(MSIHANDLE hInstall)
117{
118 //AssertSz(0, "debug DropSmb");
119 UINT er = ERROR_SUCCESS;
120 HRESULT hr = S_OK;
121
122 LPWSTR pwzData = NULL;
123 LPWSTR pwz = NULL;
124 LPWSTR pwzFsKey = NULL;
125 SCA_SMBP ssp = {0};
126
127 hr = WcaInitialize(hInstall, "DropSmb");
128 ExitOnFailure(hr, "failed to initialize");
129
130 hr = WcaGetProperty( L"CustomActionData", &pwzData);
131 ExitOnFailure(hr, "failed to get CustomActionData");
132
133 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData);
134
135 pwz = pwzData;
136 hr = WcaReadStringFromCaData(&pwz, &pwzFsKey); // share name
137 ExitOnFailure(hr, "failed to read share name");
138
139 ssp.wzKey = pwzFsKey;
140
141 hr = ScaDropSmb(&ssp);
142 MessageExitOnFailure(hr, msierrSMBFailedDrop, "failed to delete share: '%ls'", pwzFsKey);
143
144 hr = WcaProgressMessage(COST_SMB_DROPSMB, FALSE);
145
146LExit:
147 ReleaseStr(pwzFsKey);
148 ReleaseStr(pwzData);
149
150 if (FAILED(hr))
151 {
152 er = ERROR_INSTALL_FAILURE;
153 }
154 return WcaFinalize(er);
155}
156
157
158static HRESULT AddUserToGroup(
159 __in LPWSTR wzUser,
160 __in LPCWSTR wzUserDomain,
161 __in LPCWSTR wzGroup,
162 __in LPCWSTR wzGroupDomain
163 )
164{
165 Assert(wzUser && *wzUser && wzUserDomain && wzGroup && *wzGroup && wzGroupDomain);
166
167 HRESULT hr = S_OK;
168 IADsGroup *pGroup = NULL;
169 BSTR bstrUser = NULL;
170 BSTR bstrGroup = NULL;
171 LPCWSTR wz = NULL;
172 LPWSTR pwzUser = NULL;
173 LOCALGROUP_MEMBERS_INFO_3 lgmi;
174
175 if (*wzGroupDomain)
176 {
177 wz = wzGroupDomain;
178 }
179
180 // Try adding it to the global group first
181 UINT ui = ::NetGroupAddUser(wz, wzGroup, wzUser);
182 if (NERR_GroupNotFound == ui)
183 {
184 // Try adding it to the local group
185 if (wzUserDomain)
186 {
187 hr = StrAllocFormatted(&pwzUser, L"%s\\%s", wzUserDomain, wzUser);
188 ExitOnFailure(hr, "failed to allocate user domain string");
189 }
190
191 lgmi.lgrmi3_domainandname = (NULL == pwzUser ? wzUser : pwzUser);
192 ui = ::NetLocalGroupAddMembers(wz, wzGroup, 3 , reinterpret_cast<LPBYTE>(&lgmi), 1);
193 }
194 hr = HRESULT_FROM_WIN32(ui);
195 if (HRESULT_FROM_WIN32(ERROR_MEMBER_IN_ALIAS) == hr) // if they're already a member of the group don't report an error
196 hr = S_OK;
197
198 //
199 // If we failed, try active directory
200 //
201 if (FAILED(hr))
202 {
203 WcaLog(LOGMSG_VERBOSE, "Failed to add user: %ls, domain %ls to group: %ls, domain: %ls with error 0x%x. Attempting to use Active Directory", wzUser, wzUserDomain, wzGroup, wzGroupDomain, hr);
204
205 hr = UserCreateADsPath(wzUserDomain, wzUser, &bstrUser);
206 ExitOnFailure(hr, "failed to create user ADsPath for user: %ls domain: %ls", wzUser, wzUserDomain);
207
208 hr = UserCreateADsPath(wzGroupDomain, wzGroup, &bstrGroup);
209 ExitOnFailure(hr, "failed to create group ADsPath for group: %ls domain: %ls", wzGroup, wzGroupDomain);
210
211 hr = ::ADsGetObject(bstrGroup,IID_IADsGroup, reinterpret_cast<void**>(&pGroup));
212 ExitOnFailure(hr, "Failed to get group '%ls'.", reinterpret_cast<WCHAR*>(bstrGroup) );
213
214 hr = pGroup->Add(bstrUser);
215 if ((HRESULT_FROM_WIN32(ERROR_OBJECT_ALREADY_EXISTS) == hr) || (HRESULT_FROM_WIN32(ERROR_MEMBER_IN_ALIAS) == hr))
216 hr = S_OK;
217
218 ExitOnFailure(hr, "Failed to add user %ls to group '%ls'.", reinterpret_cast<WCHAR*>(bstrUser), reinterpret_cast<WCHAR*>(bstrGroup) );
219 }
220
221LExit:
222 ReleaseObject(pGroup);
223 ReleaseBSTR(bstrUser);
224 ReleaseBSTR(bstrGroup);
225
226 return hr;
227}
228
229static HRESULT RemoveUserFromGroup(
230 __in LPWSTR wzUser,
231 __in LPCWSTR wzUserDomain,
232 __in LPCWSTR wzGroup,
233 __in LPCWSTR wzGroupDomain
234 )
235{
236 Assert(wzUser && *wzUser && wzUserDomain && wzGroup && *wzGroup && wzGroupDomain);
237
238 HRESULT hr = S_OK;
239 IADsGroup *pGroup = NULL;
240 BSTR bstrUser = NULL;
241 BSTR bstrGroup = NULL;
242 LPCWSTR wz = NULL;
243 LPWSTR pwzUser = NULL;
244 LOCALGROUP_MEMBERS_INFO_3 lgmi;
245
246 if (*wzGroupDomain)
247 {
248 wz = wzGroupDomain;
249 }
250
251 // Try removing it from the global group first
252 UINT ui = ::NetGroupDelUser(wz, wzGroup, wzUser);
253 if (NERR_GroupNotFound == ui)
254 {
255 // Try removing it from the local group
256 if (wzUserDomain)
257 {
258 hr = StrAllocFormatted(&pwzUser, L"%s\\%s", wzUserDomain, wzUser);
259 ExitOnFailure(hr, "failed to allocate user domain string");
260 }
261
262 lgmi.lgrmi3_domainandname = (NULL == pwzUser ? wzUser : pwzUser);
263 ui = ::NetLocalGroupDelMembers(wz, wzGroup, 3 , reinterpret_cast<LPBYTE>(&lgmi), 1);
264 }
265 hr = HRESULT_FROM_WIN32(ui);
266
267 //
268 // If we failed, try active directory
269 //
270 if (FAILED(hr))
271 {
272 WcaLog(LOGMSG_VERBOSE, "Failed to remove user: %ls, domain %ls from group: %ls, domain: %ls with error 0x%x. Attempting to use Active Directory", wzUser, wzUserDomain, wzGroup, wzGroupDomain, hr);
273
274 hr = UserCreateADsPath(wzUserDomain, wzUser, &bstrUser);
275 ExitOnFailure(hr, "failed to create user ADsPath in order to remove user: %ls domain: %ls from a group", wzUser, wzUserDomain);
276
277 hr = UserCreateADsPath(wzGroupDomain, wzGroup, &bstrGroup);
278 ExitOnFailure(hr, "failed to create group ADsPath in order to remove user from group: %ls domain: %ls", wzGroup, wzGroupDomain);
279
280 hr = ::ADsGetObject(bstrGroup,IID_IADsGroup, reinterpret_cast<void**>(&pGroup));
281 ExitOnFailure(hr, "Failed to get group '%ls'.", reinterpret_cast<WCHAR*>(bstrGroup) );
282
283 hr = pGroup->Remove(bstrUser);
284 ExitOnFailure(hr, "Failed to remove user %ls from group '%ls'.", reinterpret_cast<WCHAR*>(bstrUser), reinterpret_cast<WCHAR*>(bstrGroup) );
285 }
286
287LExit:
288 ReleaseObject(pGroup);
289 ReleaseBSTR(bstrUser);
290 ReleaseBSTR(bstrGroup);
291
292 return hr;
293}
294
295
296static HRESULT GetUserHasRight(
297 __in LSA_HANDLE hPolicy,
298 __in PSID pUserSid,
299 __in LPWSTR wzRight,
300 __out BOOL* fHasRight
301)
302{
303 HRESULT hr = S_OK;
304 NTSTATUS nt = 0;
305 LSA_UNICODE_STRING lucPrivilege = { 0 };
306 PLSA_ENUMERATION_INFORMATION rgSids = NULL;
307 ULONG cSids = 0;
308 *fHasRight = FALSE;
309
310 lucPrivilege.Buffer = wzRight;
311 lucPrivilege.Length = static_cast<USHORT>(lstrlenW(lucPrivilege.Buffer) * sizeof(WCHAR));
312 lucPrivilege.MaximumLength = (lucPrivilege.Length + 1) * sizeof(WCHAR);
313
314 nt = ::LsaEnumerateAccountsWithUserRight(hPolicy, &lucPrivilege, reinterpret_cast<PVOID*>(&rgSids), &cSids);
315 hr = HRESULT_FROM_WIN32(::LsaNtStatusToWinError(nt));
316 ExitOnFailure(hr, "Failed to enumerate users for right: %ls", lucPrivilege.Buffer);
317
318 for (DWORD i = 0; i < cSids; ++i)
319 {
320 PLSA_ENUMERATION_INFORMATION pInfo = rgSids + i;
321 if (::EqualSid(pUserSid, pInfo->Sid))
322 {
323 *fHasRight = TRUE;
324 break;
325 }
326 }
327
328LExit:
329 if (rgSids)
330 {
331 ::LsaFreeMemory(rgSids);
332 }
333
334 return hr;
335}
336
337
338static HRESULT GetExistingUserRightsAssignments(
339 __in_opt LPCWSTR wzDomain,
340 __in LPCWSTR wzName,
341 __inout int* iAttributes
342)
343{
344 HRESULT hr = S_OK;
345 NTSTATUS nt = 0;
346 BOOL fHasRight = FALSE;
347
348 LSA_HANDLE hPolicy = NULL;
349 LSA_OBJECT_ATTRIBUTES objectAttributes = { 0 };
350
351 LPWSTR pwzUser = NULL;
352 PSID psid = NULL;
353
354 if (wzDomain && *wzDomain)
355 {
356 hr = StrAllocFormatted(&pwzUser, L"%s\\%s", wzDomain, wzName);
357 ExitOnFailure(hr, "Failed to allocate user with domain string");
358 }
359 else
360 {
361 hr = StrAllocString(&pwzUser, wzName, 0);
362 ExitOnFailure(hr, "Failed to allocate string from user name.");
363 }
364
365 hr = AclGetAccountSid(NULL, pwzUser, &psid);
366 ExitOnFailure(hr, "Failed to get SID for user: %ls", pwzUser);
367
368 nt = ::LsaOpenPolicy(NULL, &objectAttributes, POLICY_LOOKUP_NAMES | POLICY_VIEW_LOCAL_INFORMATION, &hPolicy);
369 hr = HRESULT_FROM_WIN32(::LsaNtStatusToWinError(nt));
370 ExitOnFailure(hr, "Failed to open LSA policy store");
371
372 hr = GetUserHasRight(hPolicy, psid, L"SeServiceLogonRight", &fHasRight);
373 ExitOnFailure(hr, "Failed to check LogonAsService right");
374
375 if (fHasRight)
376 {
377 *iAttributes |= SCAU_ALLOW_LOGON_AS_SERVICE;
378 }
379
380 hr = GetUserHasRight(hPolicy, psid, L"SeBatchLogonRight", &fHasRight);
381 ExitOnFailure(hr, "Failed to check LogonAsBatchJob right");
382
383 if (fHasRight)
384 {
385 *iAttributes |= SCAU_ALLOW_LOGON_AS_BATCH;
386 }
387
388LExit:
389 if (hPolicy)
390 {
391 ::LsaClose(hPolicy);
392 }
393
394 ReleaseSid(psid);
395 ReleaseStr(pwzUser);
396 return hr;
397}
398
399
400static HRESULT ModifyUserLocalServiceRight(
401 __in_opt LPCWSTR wzDomain,
402 __in LPCWSTR wzName,
403 __in BOOL fAdd
404 )
405{
406 HRESULT hr = S_OK;
407 NTSTATUS nt = 0;
408
409 LPWSTR pwzUser = NULL;
410 PSID psid = NULL;
411 LSA_HANDLE hPolicy = NULL;
412 LSA_OBJECT_ATTRIBUTES ObjectAttributes = { 0 };
413 LSA_UNICODE_STRING lucPrivilege = { 0 };
414
415 if (wzDomain && *wzDomain)
416 {
417 hr = StrAllocFormatted(&pwzUser, L"%s\\%s", wzDomain, wzName);
418 ExitOnFailure(hr, "Failed to allocate user with domain string");
419 }
420 else
421 {
422 hr = StrAllocString(&pwzUser, wzName, 0);
423 ExitOnFailure(hr, "Failed to allocate string from user name.");
424 }
425
426 hr = AclGetAccountSid(NULL, pwzUser, &psid);
427 ExitOnFailure(hr, "Failed to get SID for user: %ls", pwzUser);
428
429 nt = ::LsaOpenPolicy(NULL, &ObjectAttributes, POLICY_ALL_ACCESS, &hPolicy);
430 hr = HRESULT_FROM_WIN32(::LsaNtStatusToWinError(nt));
431 ExitOnFailure(hr, "Failed to open LSA policy store.");
432
433 lucPrivilege.Buffer = L"SeServiceLogonRight";
434 lucPrivilege.Length = static_cast<USHORT>(lstrlenW(lucPrivilege.Buffer) * sizeof(WCHAR));
435 lucPrivilege.MaximumLength = (lucPrivilege.Length + 1) * sizeof(WCHAR);
436
437 if (fAdd)
438 {
439 nt = ::LsaAddAccountRights(hPolicy, psid, &lucPrivilege, 1);
440 hr = HRESULT_FROM_WIN32(::LsaNtStatusToWinError(nt));
441 ExitOnFailure(hr, "Failed to add 'logon as service' bit to user: %ls", pwzUser);
442 }
443 else
444 {
445 nt = ::LsaRemoveAccountRights(hPolicy, psid, FALSE, &lucPrivilege, 1);
446 hr = HRESULT_FROM_WIN32(::LsaNtStatusToWinError(nt));
447 ExitOnFailure(hr, "Failed to remove 'logon as service' bit from user: %ls", pwzUser);
448 }
449
450LExit:
451 if (hPolicy)
452 {
453 ::LsaClose(hPolicy);
454 }
455
456 ReleaseSid(psid);
457 ReleaseStr(pwzUser);
458 return hr;
459}
460
461
462static HRESULT ModifyUserLocalBatchRight(
463 __in_opt LPCWSTR wzDomain,
464 __in LPCWSTR wzName,
465 __in BOOL fAdd
466 )
467{
468 HRESULT hr = S_OK;
469 NTSTATUS nt = 0;
470
471 LPWSTR pwzUser = NULL;
472 PSID psid = NULL;
473 LSA_HANDLE hPolicy = NULL;
474 LSA_OBJECT_ATTRIBUTES ObjectAttributes = { 0 };
475 LSA_UNICODE_STRING lucPrivilege = { 0 };
476
477 if (wzDomain && *wzDomain)
478 {
479 hr = StrAllocFormatted(&pwzUser, L"%s\\%s", wzDomain, wzName);
480 ExitOnFailure(hr, "Failed to allocate user with domain string");
481 }
482 else
483 {
484 hr = StrAllocString(&pwzUser, wzName, 0);
485 ExitOnFailure(hr, "Failed to allocate string from user name.");
486 }
487
488 hr = AclGetAccountSid(NULL, pwzUser, &psid);
489 ExitOnFailure(hr, "Failed to get SID for user: %ls", pwzUser);
490
491 nt = ::LsaOpenPolicy(NULL, &ObjectAttributes, POLICY_ALL_ACCESS, &hPolicy);
492 hr = HRESULT_FROM_WIN32(::LsaNtStatusToWinError(nt));
493 ExitOnFailure(hr, "Failed to open LSA policy store.");
494
495 lucPrivilege.Buffer = L"SeBatchLogonRight";
496 lucPrivilege.Length = static_cast<USHORT>(lstrlenW(lucPrivilege.Buffer) * sizeof(WCHAR));
497 lucPrivilege.MaximumLength = (lucPrivilege.Length + 1) * sizeof(WCHAR);
498
499 if (fAdd)
500 {
501 nt = ::LsaAddAccountRights(hPolicy, psid, &lucPrivilege, 1);
502 hr = HRESULT_FROM_WIN32(::LsaNtStatusToWinError(nt));
503 ExitOnFailure(hr, "Failed to add 'logon as batch job' bit to user: %ls", pwzUser);
504 }
505 else
506 {
507 nt = ::LsaRemoveAccountRights(hPolicy, psid, FALSE, &lucPrivilege, 1);
508 hr = HRESULT_FROM_WIN32(::LsaNtStatusToWinError(nt));
509 ExitOnFailure(hr, "Failed to remove 'logon as batch job' bit from user: %ls", pwzUser);
510 }
511
512 LExit:
513 if (hPolicy)
514 {
515 ::LsaClose(hPolicy);
516 }
517
518 ReleaseSid(psid);
519 ReleaseStr(pwzUser);
520 return hr;
521}
522
523static void SetUserPasswordAndAttributes(
524 __in USER_INFO_1* puserInfo,
525 __in LPWSTR wzPassword,
526 __in int iAttributes
527 )
528{
529 Assert(puserInfo);
530
531 // Set the User's password
532 puserInfo->usri1_password = wzPassword;
533
534 // Apply the Attributes
535 if (SCAU_DONT_EXPIRE_PASSWRD & iAttributes)
536 {
537 puserInfo->usri1_flags |= UF_DONT_EXPIRE_PASSWD;
538 }
539 else
540 {
541 puserInfo->usri1_flags &= ~UF_DONT_EXPIRE_PASSWD;
542 }
543
544 if (SCAU_PASSWD_CANT_CHANGE & iAttributes)
545 {
546 puserInfo->usri1_flags |= UF_PASSWD_CANT_CHANGE;
547 }
548 else
549 {
550 puserInfo->usri1_flags &= ~UF_PASSWD_CANT_CHANGE;
551 }
552
553 if (SCAU_DISABLE_ACCOUNT & iAttributes)
554 {
555 puserInfo->usri1_flags |= UF_ACCOUNTDISABLE;
556 }
557 else
558 {
559 puserInfo->usri1_flags &= ~UF_ACCOUNTDISABLE;
560 }
561
562 if (SCAU_PASSWD_CHANGE_REQD_ON_LOGIN & iAttributes) // TODO: for some reason this doesn't work
563 {
564 puserInfo->usri1_flags |= UF_PASSWORD_EXPIRED;
565 }
566 else
567 {
568 puserInfo->usri1_flags &= ~UF_PASSWORD_EXPIRED;
569 }
570}
571
572
573static HRESULT RemoveUserInternal(
574 LPWSTR wzGroupCaData,
575 LPWSTR wzDomain,
576 LPWSTR wzName,
577 int iAttributes
578)
579{
580 HRESULT hr = S_OK;
581 UINT er = ERROR_SUCCESS;
582
583 LPWSTR pwz = NULL;
584 LPWSTR pwzGroup = NULL;
585 LPWSTR pwzGroupDomain = NULL;
586 LPCWSTR wz = NULL;
587 PDOMAIN_CONTROLLER_INFOW pDomainControllerInfo = NULL;
588
589 //
590 // Remove the logon as service privilege.
591 //
592 if (SCAU_ALLOW_LOGON_AS_SERVICE & iAttributes)
593 {
594 hr = ModifyUserLocalServiceRight(wzDomain, wzName, FALSE);
595 if (FAILED(hr))
596 {
597 WcaLogError(hr, "Failed to remove logon as service right from user, continuing...");
598 hr = S_OK;
599 }
600 }
601
602 if (SCAU_ALLOW_LOGON_AS_BATCH & iAttributes)
603 {
604 hr = ModifyUserLocalBatchRight(wzDomain, wzName, FALSE);
605 if (FAILED(hr))
606 {
607 WcaLogError(hr, "Failed to remove logon as batch job right from user, continuing...");
608 hr = S_OK;
609 }
610 }
611
612 //
613 // Remove the User Account if the user was created by us.
614 //
615 if (!(SCAU_DONT_CREATE_USER & iAttributes))
616 {
617 if (wzDomain && *wzDomain)
618 {
619 er = ::DsGetDcNameW(NULL, (LPCWSTR)wzDomain, NULL, NULL, NULL, &pDomainControllerInfo);
620 if (RPC_S_SERVER_UNAVAILABLE == er)
621 {
622 // MSDN says, if we get the above error code, try again with the "DS_FORCE_REDISCOVERY" flag
623 er = ::DsGetDcNameW(NULL, (LPCWSTR)wzDomain, NULL, NULL, DS_FORCE_REDISCOVERY, &pDomainControllerInfo);
624 }
625 if (ERROR_SUCCESS == er)
626 {
627 wz = pDomainControllerInfo->DomainControllerName + 2; //Add 2 so that we don't get the \\ prefix
628 }
629 else
630 {
631 wz = wzDomain;
632 }
633 }
634
635 er = ::NetUserDel(wz, wzName);
636 if (NERR_UserNotFound == er)
637 {
638 er = NERR_Success;
639 }
640 ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "failed to delete user account: %ls", wzName);
641 }
642 else
643 {
644 //
645 // Remove the user from the groups
646 //
647 pwz = wzGroupCaData;
648 while (S_OK == (hr = WcaReadStringFromCaData(&pwz, &pwzGroup)))
649 {
650 hr = WcaReadStringFromCaData(&pwz, &pwzGroupDomain);
651
652 if (FAILED(hr))
653 {
654 WcaLogError(hr, "failed to get domain for group: %ls, continuing anyway.", pwzGroup);
655 }
656 else
657 {
658 hr = RemoveUserFromGroup(wzName, wzDomain, pwzGroup, pwzGroupDomain);
659 if (FAILED(hr))
660 {
661 WcaLogError(hr, "failed to remove user: %ls from group %ls, continuing anyway.", wzName, pwzGroup);
662 }
663 }
664 }
665
666 if (E_NOMOREITEMS == hr) // if there are no more items, all is well
667 {
668 hr = S_OK;
669 }
670
671 ExitOnFailure(hr, "failed to get next group from which to remove user:%ls", wzName);
672 }
673
674LExit:
675 if (pDomainControllerInfo)
676 {
677 ::NetApiBufferFree(static_cast<LPVOID>(pDomainControllerInfo));
678 }
679
680 return hr;
681}
682
683
684/********************************************************************
685 CreateUser - CUSTOM ACTION ENTRY POINT for creating users
686
687 Input: deferred CustomActionData - UserName\tDomain\tPassword\tAttributes\tGroupName\tDomain\tGroupName\tDomain...
688 * *****************************************************************/
689extern "C" UINT __stdcall CreateUser(
690 __in MSIHANDLE hInstall
691 )
692{
693 //AssertSz(0, "Debug CreateUser");
694
695 HRESULT hr = S_OK;
696 UINT er = ERROR_SUCCESS;
697
698 LPWSTR pwzData = NULL;
699 LPWSTR pwz = NULL;
700 LPWSTR pwzName = NULL;
701 LPWSTR pwzDomain = NULL;
702 LPWSTR pwzScriptKey = NULL;
703 LPWSTR pwzPassword = NULL;
704 LPWSTR pwzGroup = NULL;
705 LPWSTR pwzGroupDomain = NULL;
706 PDOMAIN_CONTROLLER_INFOW pDomainControllerInfo = NULL;
707 int iAttributes = 0;
708 BOOL fInitializedCom = FALSE;
709
710 WCA_CASCRIPT_HANDLE hRollbackScript = NULL;
711 int iOriginalAttributes = 0;
712 int iRollbackAttributes = 0;
713
714 USER_INFO_1 userInfo;
715 USER_INFO_1* puserInfo = NULL;
716 DWORD dw;
717 LPCWSTR wz = NULL;
718
719 hr = WcaInitialize(hInstall, "CreateUser");
720 ExitOnFailure(hr, "failed to initialize");
721
722 hr = ::CoInitialize(NULL);
723 ExitOnFailure(hr, "failed to initialize COM");
724 fInitializedCom = TRUE;
725
726 hr = WcaGetProperty( L"CustomActionData", &pwzData);
727 ExitOnFailure(hr, "failed to get CustomActionData");
728
729 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData);
730
731 //
732 // Read in the CustomActionData
733 //
734 pwz = pwzData;
735 hr = WcaReadStringFromCaData(&pwz, &pwzName);
736 ExitOnFailure(hr, "failed to read user name from custom action data");
737
738 hr = WcaReadStringFromCaData(&pwz, &pwzDomain);
739 ExitOnFailure(hr, "failed to read domain from custom action data");
740
741 hr = WcaReadIntegerFromCaData(&pwz, &iAttributes);
742 ExitOnFailure(hr, "failed to read attributes from custom action data");
743
744 hr = WcaReadStringFromCaData(&pwz, &pwzScriptKey);
745 ExitOnFailure(hr, "failed to read encoding key from custom action data");
746
747 hr = WcaReadStringFromCaData(&pwz, &pwzPassword);
748 ExitOnFailure(hr, "failed to read password from custom action data");
749
750 // There is no rollback scheduled if the key is empty.
751 // Best effort to get original configuration and save it in the script so rollback can restore it.
752 if (*pwzScriptKey)
753 {
754 hr = WcaCaScriptCreate(WCA_ACTION_INSTALL, WCA_CASCRIPT_ROLLBACK, FALSE, pwzScriptKey, FALSE, &hRollbackScript);
755 ExitOnFailure(hr, "Failed to open rollback CustomAction script.");
756
757 iRollbackAttributes = 0;
758 hr = GetExistingUserRightsAssignments(pwzDomain, pwzName, &iOriginalAttributes);
759 if (FAILED(hr))
760 {
761 WcaLogError(hr, "failed to get existing user rights: %ls, continuing anyway.", pwzName);
762 }
763 else
764 {
765 if (!(SCAU_ALLOW_LOGON_AS_SERVICE & iOriginalAttributes) && (SCAU_ALLOW_LOGON_AS_SERVICE & iAttributes))
766 {
767 iRollbackAttributes |= SCAU_ALLOW_LOGON_AS_SERVICE;
768 }
769 if (!(SCAU_ALLOW_LOGON_AS_BATCH & iOriginalAttributes) && (SCAU_ALLOW_LOGON_AS_BATCH & iAttributes))
770 {
771 iRollbackAttributes |= SCAU_ALLOW_LOGON_AS_BATCH;
772 }
773 }
774
775 hr = WcaCaScriptWriteNumber(hRollbackScript, iRollbackAttributes);
776 ExitOnFailure(hr, "Failed to add data to rollback script.");
777
778 // Nudge the system to get all our rollback data written to disk.
779 WcaCaScriptFlush(hRollbackScript);
780 }
781
782 if (!(SCAU_DONT_CREATE_USER & iAttributes))
783 {
784 ::ZeroMemory(&userInfo, sizeof(USER_INFO_1));
785 userInfo.usri1_name = pwzName;
786 userInfo.usri1_priv = USER_PRIV_USER;
787 userInfo.usri1_flags = UF_SCRIPT;
788 userInfo.usri1_home_dir = NULL;
789 userInfo.usri1_comment = NULL;
790 userInfo.usri1_script_path = NULL;
791
792 SetUserPasswordAndAttributes(&userInfo, pwzPassword, iAttributes);
793
794 //
795 // Create the User
796 //
797 if (pwzDomain && *pwzDomain)
798 {
799 er = ::DsGetDcNameW( NULL, (LPCWSTR)pwzDomain, NULL, NULL, NULL, &pDomainControllerInfo );
800 if (RPC_S_SERVER_UNAVAILABLE == er)
801 {
802 // MSDN says, if we get the above error code, try again with the "DS_FORCE_REDISCOVERY" flag
803 er = ::DsGetDcNameW( NULL, (LPCWSTR)pwzDomain, NULL, NULL, DS_FORCE_REDISCOVERY, &pDomainControllerInfo );
804 }
805 if (ERROR_SUCCESS == er)
806 {
807 wz = pDomainControllerInfo->DomainControllerName + 2; //Add 2 so that we don't get the \\ prefix
808 }
809 else
810 {
811 wz = pwzDomain;
812 }
813 }
814
815 er = ::NetUserAdd(wz, 1, reinterpret_cast<LPBYTE>(&userInfo), &dw);
816 if (NERR_UserExists == er)
817 {
818 if (SCAU_UPDATE_IF_EXISTS & iAttributes)
819 {
820 er = ::NetUserGetInfo(wz, pwzName, 1, reinterpret_cast<LPBYTE*>(&puserInfo));
821 if (NERR_Success == er)
822 {
823 // Change the existing user's password and attributes again then try
824 // to update user with this new data
825 SetUserPasswordAndAttributes(puserInfo, pwzPassword, iAttributes);
826
827 er = ::NetUserSetInfo(wz, pwzName, 1, reinterpret_cast<LPBYTE>(puserInfo), &dw);
828 }
829 }
830 else if (!(SCAU_FAIL_IF_EXISTS & iAttributes))
831 {
832 er = NERR_Success;
833 }
834 }
835 else if (NERR_PasswordTooShort == er || NERR_PasswordTooLong == er)
836 {
837 MessageExitOnFailure(hr = HRESULT_FROM_WIN32(er), msierrUSRFailedUserCreatePswd, "failed to create user: %ls due to invalid password.", pwzName);
838 }
839 MessageExitOnFailure(hr = HRESULT_FROM_WIN32(er), msierrUSRFailedUserCreate, "failed to create user: %ls", pwzName);
840 }
841
842 if (SCAU_ALLOW_LOGON_AS_SERVICE & iAttributes)
843 {
844 hr = ModifyUserLocalServiceRight(pwzDomain, pwzName, TRUE);
845 MessageExitOnFailure(hr, msierrUSRFailedGrantLogonAsService, "Failed to grant logon as service rights to user: %ls", pwzName);
846 }
847
848 if (SCAU_ALLOW_LOGON_AS_BATCH & iAttributes)
849 {
850 hr = ModifyUserLocalBatchRight(pwzDomain, pwzName, TRUE);
851 MessageExitOnFailure(hr, msierrUSRFailedGrantLogonAsService, "Failed to grant logon as batch job rights to user: %ls", pwzName);
852 }
853
854 //
855 // Add the users to groups
856 //
857 while (S_OK == (hr = WcaReadStringFromCaData(&pwz, &pwzGroup)))
858 {
859 hr = WcaReadStringFromCaData(&pwz, &pwzGroupDomain);
860 ExitOnFailure(hr, "failed to get domain for group: %ls", pwzGroup);
861
862 hr = AddUserToGroup(pwzName, pwzDomain, pwzGroup, pwzGroupDomain);
863 MessageExitOnFailure(hr, msierrUSRFailedUserGroupAdd, "failed to add user: %ls to group %ls", pwzName, pwzGroup);
864 }
865 if (E_NOMOREITEMS == hr) // if there are no more items, all is well
866 {
867 hr = S_OK;
868 }
869 ExitOnFailure(hr, "failed to get next group in which to include user:%ls", pwzName);
870
871LExit:
872 WcaCaScriptClose(hRollbackScript, WCA_CASCRIPT_CLOSE_PRESERVE);
873
874 if (puserInfo)
875 {
876 ::NetApiBufferFree((LPVOID)puserInfo);
877 }
878
879 if (pDomainControllerInfo)
880 {
881 ::NetApiBufferFree((LPVOID)pDomainControllerInfo);
882 }
883
884 ReleaseStr(pwzData);
885 ReleaseStr(pwzName);
886 ReleaseStr(pwzDomain);
887 ReleaseStr(pwzScriptKey);
888 ReleaseStr(pwzPassword);
889 ReleaseStr(pwzGroup);
890 ReleaseStr(pwzGroupDomain);
891
892 if (fInitializedCom)
893 {
894 ::CoUninitialize();
895 }
896
897 if (SCAU_NON_VITAL & iAttributes)
898 {
899 er = ERROR_SUCCESS;
900 }
901 else if (FAILED(hr))
902 {
903 er = ERROR_INSTALL_FAILURE;
904 }
905
906 return WcaFinalize(er);
907}
908
909
910/********************************************************************
911 CreateUserRollback - CUSTOM ACTION ENTRY POINT for CreateUser rollback
912
913 * *****************************************************************/
914extern "C" UINT __stdcall CreateUserRollback(
915 MSIHANDLE hInstall
916)
917{
918 //AssertSz(0, "Debug CreateUserRollback");
919
920 HRESULT hr = S_OK;
921 UINT er = ERROR_SUCCESS;
922
923 LPWSTR pwzData = NULL;
924 LPWSTR pwz = NULL;
925 LPWSTR pwzName = NULL;
926 LPWSTR pwzDomain = NULL;
927 LPWSTR pwzScriptKey = NULL;
928 int iAttributes = 0;
929 BOOL fInitializedCom = FALSE;
930
931 WCA_CASCRIPT_HANDLE hRollbackScript = NULL;
932 LPWSTR pwzRollbackData = NULL;
933 int iOriginalAttributes = 0;
934
935 hr = WcaInitialize(hInstall, "CreateUserRollback");
936 ExitOnFailure(hr, "failed to initialize");
937
938 hr = ::CoInitialize(NULL);
939 ExitOnFailure(hr, "failed to initialize COM");
940 fInitializedCom = TRUE;
941
942 hr = WcaGetProperty(L"CustomActionData", &pwzData);
943 ExitOnFailure(hr, "failed to get CustomActionData");
944
945 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData);
946
947 //
948 // Read in the CustomActionData
949 //
950 pwz = pwzData;
951 hr = WcaReadStringFromCaData(&pwz, &pwzScriptKey);
952 ExitOnFailure(hr, "failed to read encoding key from custom action data");
953
954 hr = WcaReadStringFromCaData(&pwz, &pwzName);
955 ExitOnFailure(hr, "failed to read name from custom action data");
956
957 hr = WcaReadStringFromCaData(&pwz, &pwzDomain);
958 ExitOnFailure(hr, "failed to read domain from custom action data");
959
960 hr = WcaReadIntegerFromCaData(&pwz, &iAttributes);
961 ExitOnFailure(hr, "failed to read attributes from custom action data");
962
963 // Best effort to read original configuration from CreateUser.
964 hr = WcaCaScriptOpen(WCA_ACTION_INSTALL, WCA_CASCRIPT_ROLLBACK, FALSE, pwzScriptKey, &hRollbackScript);
965 if (FAILED(hr))
966 {
967 WcaLogError(hr, "Failed to open rollback CustomAction script, continuing anyway.");
968 }
969 else
970 {
971 hr = WcaCaScriptReadAsCustomActionData(hRollbackScript, &pwzRollbackData);
972 if (FAILED(hr))
973 {
974 WcaLogError(hr, "Failed to read rollback script into CustomAction data, continuing anyway.");
975 }
976 else
977 {
978 WcaLog(LOGMSG_TRACEONLY, "Rollback Data: %ls", pwzRollbackData);
979
980 pwz = pwzRollbackData;
981 hr = WcaReadIntegerFromCaData(&pwz, &iOriginalAttributes);
982 if (FAILED(hr))
983 {
984 WcaLogError(hr, "failed to read attributes from rollback data, continuing anyway");
985 }
986 else
987 {
988 iAttributes |= iOriginalAttributes;
989 }
990 }
991 }
992
993 hr = RemoveUserInternal(pwz, pwzDomain, pwzName, iAttributes);
994
995LExit:
996 WcaCaScriptClose(hRollbackScript, WCA_CASCRIPT_CLOSE_DELETE);
997
998 ReleaseStr(pwzData);
999 ReleaseStr(pwzName);
1000 ReleaseStr(pwzDomain);
1001 ReleaseStr(pwzScriptKey);
1002 ReleaseStr(pwzRollbackData);
1003
1004 if (fInitializedCom)
1005 {
1006 ::CoUninitialize();
1007 }
1008
1009 if (FAILED(hr))
1010 {
1011 er = ERROR_INSTALL_FAILURE;
1012 }
1013
1014 return WcaFinalize(er);
1015}
1016
1017
1018/********************************************************************
1019 RemoveUser - CUSTOM ACTION ENTRY POINT for removing users
1020
1021 Input: deferred CustomActionData - Name\tDomain
1022 * *****************************************************************/
1023extern "C" UINT __stdcall RemoveUser(
1024 MSIHANDLE hInstall
1025)
1026{
1027 //AssertSz(0, "Debug RemoveUser");
1028
1029 HRESULT hr = S_OK;
1030 UINT er = ERROR_SUCCESS;
1031
1032 LPWSTR pwzData = NULL;
1033 LPWSTR pwz = NULL;
1034 LPWSTR pwzName = NULL;
1035 LPWSTR pwzDomain = NULL;
1036 int iAttributes = 0;
1037 BOOL fInitializedCom = FALSE;
1038
1039 hr = WcaInitialize(hInstall, "RemoveUser");
1040 ExitOnFailure(hr, "failed to initialize");
1041
1042 hr = ::CoInitialize(NULL);
1043 ExitOnFailure(hr, "failed to initialize COM");
1044 fInitializedCom = TRUE;
1045
1046 hr = WcaGetProperty(L"CustomActionData", &pwzData);
1047 ExitOnFailure(hr, "failed to get CustomActionData");
1048
1049 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData);
1050
1051 //
1052 // Read in the CustomActionData
1053 //
1054 pwz = pwzData;
1055 hr = WcaReadStringFromCaData(&pwz, &pwzName);
1056 ExitOnFailure(hr, "failed to read name from custom action data");
1057
1058 hr = WcaReadStringFromCaData(&pwz, &pwzDomain);
1059 ExitOnFailure(hr, "failed to read domain from custom action data");
1060
1061 hr = WcaReadIntegerFromCaData(&pwz, &iAttributes);
1062 ExitOnFailure(hr, "failed to read attributes from custom action data");
1063
1064 hr = RemoveUserInternal(pwz, pwzDomain, pwzName, iAttributes);
1065
1066LExit:
1067 ReleaseStr(pwzData);
1068 ReleaseStr(pwzName);
1069 ReleaseStr(pwzDomain);
1070
1071 if (fInitializedCom)
1072 {
1073 ::CoUninitialize();
1074 }
1075
1076 if (FAILED(hr))
1077 {
1078 er = ERROR_INSTALL_FAILURE;
1079 }
1080
1081 return WcaFinalize(er);
1082}
diff --git a/src/ext/Util/ca/scamanifest.cpp b/src/ext/Util/ca/scamanifest.cpp
new file mode 100644
index 00000000..adb8d3d3
--- /dev/null
+++ b/src/ext/Util/ca/scamanifest.cpp
@@ -0,0 +1,377 @@
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
5LPCWSTR vcsPerfmonManifestQuery = L"SELECT `Component_`, `File`, `ResourceFileDirectory` FROM `Wix4PerfmonManifest`";
6LPCWSTR vcsEventManifestQuery = L"SELECT `Component_`, `File` FROM `Wix4EventManifest`";
7enum ePerfMonManifestQuery { pfmComponent = 1, pfmFile, pfmResourceFileDir };
8enum eEventManifestQuery { emComponent = 1, emFile};
9
10BOOL IsVistaOrAbove()
11{
12 OSVERSIONINFO osvi;
13 ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
14 osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
15 #pragma warning(suppress: 4996) //TODO: use non-deprecated function to check OS version
16 if (!::GetVersionEx(&osvi))
17 {
18 return false;
19 }
20 return osvi.dwMajorVersion >= 6;
21}
22
23
24/********************************************************************
25 ConfigurePerfmonManifestRegister - CUSTOM ACTION ENTRY POINT for scheduling
26 Perfmon counter manifest registering
27
28********************************************************************/
29extern "C" UINT __stdcall ConfigurePerfmonManifestRegister(
30 __in MSIHANDLE hInstall
31 )
32{
33 HRESULT hr;
34 UINT er = ERROR_SUCCESS;
35
36 PMSIHANDLE hView, hRec;
37 LPWSTR pwzData = NULL, pwzResourceFilePath = NULL, pwzFile = NULL, pwzCommand = NULL;
38 INSTALLSTATE isInstalled, isAction;
39
40 hr = WcaInitialize(hInstall, "ConfigurePerfmonManifestReg");
41 ExitOnFailure(hr, "Failed to initialize");
42
43 if (!IsVistaOrAbove())
44 {
45 WcaLog(LOGMSG_VERBOSE, "Skipping ConfigurePerfmonManifestRegister() because the target system does not support perfmon manifest");
46 ExitFunction1(hr = S_FALSE);
47 }
48 // check to see if necessary tables are specified
49 if (S_OK != WcaTableExists(L"Wix4PerfmonManifest"))
50 {
51 WcaLog(LOGMSG_VERBOSE, "Skipping ConfigurePerfmonManifestRegister() because Wix4PerfmonManifest table not present");
52 ExitFunction1(hr = S_FALSE);
53 }
54
55 hr = WcaOpenExecuteView(vcsPerfmonManifestQuery, &hView);
56 ExitOnFailure(hr, "failed to open view on PerfMonManifest table");
57 while ((hr = WcaFetchRecord(hView, &hRec)) == S_OK)
58 {
59 // get component install state
60 hr = WcaGetRecordString(hRec, pfmComponent, &pwzData);
61 ExitOnFailure(hr, "failed to get Component for PerfMonManifest");
62 er = ::MsiGetComponentStateW(hInstall, pwzData, &isInstalled, &isAction);
63 hr = HRESULT_FROM_WIN32(er);
64 ExitOnFailure(hr, "failed to get Component state for PerfMonManifest");
65 if (!WcaIsInstalling(isInstalled, isAction))
66 {
67 continue;
68 }
69
70 hr = WcaGetRecordFormattedString(hRec, pfmFile, &pwzFile);
71 ExitOnFailure(hr, "failed to get File for PerfMonManifest");
72
73 hr = WcaGetRecordFormattedString(hRec, pfmResourceFileDir, &pwzResourceFilePath);
74 ExitOnFailure(hr, "failed to get ApplicationIdentity for PerfMonManifest");
75 size_t iResourcePath = lstrlenW(pwzResourceFilePath);
76 if ( iResourcePath > 0 && *(pwzResourceFilePath + iResourcePath -1) == L'\\')
77 *(pwzResourceFilePath + iResourcePath -1) = 0; //remove the trailing '\'
78
79 hr = StrAllocFormatted(&pwzCommand, L"\"unlodctr.exe\" /m:\"%s\"", pwzFile);
80 ExitOnFailure(hr, "failed to copy string in PerfMonManifest");
81
82 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"RollbackRegisterPerfmonManifest"), pwzCommand, COST_PERFMONMANIFEST_UNREGISTER);
83 ExitOnFailure(hr, "failed to schedule RollbackRegisterPerfmonManifest action");
84
85 if ( *pwzResourceFilePath )
86 {
87 hr = StrAllocFormatted(&pwzCommand, L"\"lodctr.exe\" /m:\"%s\" \"%s\"", pwzFile, pwzResourceFilePath);
88 ExitOnFailure(hr, "failed to copy string in PerfMonManifest");
89 }
90 else
91 {
92 hr = StrAllocFormatted(&pwzCommand, L"\"lodctr.exe\" /m:\"%s\"", pwzFile);
93 ExitOnFailure(hr, "failed to copy string in PerfMonManifest");
94 }
95
96 WcaLog(LOGMSG_VERBOSE, "RegisterPerfmonManifest's CustomActionData: '%ls'", pwzCommand);
97
98 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"RegisterPerfmonManifest"), pwzCommand, COST_PERFMONMANIFEST_REGISTER);
99 ExitOnFailure(hr, "failed to schedule RegisterPerfmonManifest action");
100 }
101
102 if (hr == E_NOMOREITEMS)
103 {
104 hr = S_OK;
105 }
106 ExitOnFailure(hr, "Failure while processing PerfMonManifest");
107
108 hr = S_OK;
109
110LExit:
111 ReleaseStr(pwzData);
112 ReleaseStr(pwzResourceFilePath);
113 ReleaseStr(pwzFile);
114 ReleaseStr(pwzCommand);
115
116 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
117 return WcaFinalize(er);
118}
119
120
121/********************************************************************
122 ConfigurePerfmonUninstall - CUSTOM ACTION ENTRY POINT for uninstalling
123 Perfmon counters
124
125********************************************************************/
126extern "C" UINT __stdcall ConfigurePerfmonManifestUnregister(
127 __in MSIHANDLE hInstall
128 )
129{
130 HRESULT hr;
131 UINT er = ERROR_SUCCESS;
132
133 PMSIHANDLE hView, hRec;
134 LPWSTR pwzData = NULL, pwzResourceFilePath = NULL, pwzFile = NULL, pwzCommand = NULL;
135 INSTALLSTATE isInstalled, isAction;
136
137 hr = WcaInitialize(hInstall, "ConfigurePerfmonManifestUnreg");
138 ExitOnFailure(hr, "Failed to initialize");
139
140 if (!IsVistaOrAbove())
141 {
142 WcaLog(LOGMSG_VERBOSE, "Skipping ConfigurePerfmonManifestUnregister() because the target system does not support perfmon manifest");
143 ExitFunction1(hr = S_FALSE);
144 }
145 // check to see if necessary tables are specified
146 if (WcaTableExists(L"Wix4PerfmonManifest") != S_OK)
147 {
148 WcaLog(LOGMSG_VERBOSE, "Skipping ConfigurePerfmonManifestUnregister() because Wix4PerfmonManifest table not present");
149 ExitFunction1(hr = S_FALSE);
150 }
151
152 hr = WcaOpenExecuteView(vcsPerfmonManifestQuery, &hView);
153 ExitOnFailure(hr, "failed to open view on Wix4PerfmonManifest table");
154 while ((hr = WcaFetchRecord(hView, &hRec)) == S_OK)
155 {
156 // get component install state
157 hr = WcaGetRecordString(hRec, pfmComponent, &pwzData);
158 ExitOnFailure(hr, "failed to get Component for Wix4PerfmonManifest");
159 er = ::MsiGetComponentStateW(hInstall, pwzData, &isInstalled, &isAction);
160 hr = HRESULT_FROM_WIN32(er);
161 ExitOnFailure(hr, "failed to get Component state for Wix4PerfmonManifest");
162 if (!WcaIsUninstalling(isInstalled, isAction))
163 {
164 continue;
165 }
166
167 hr = WcaGetRecordFormattedString(hRec, pfmFile, &pwzFile);
168 ExitOnFailure(hr, "failed to get File for Wix4PerfmonManifest");
169
170 hr = WcaGetRecordFormattedString(hRec, pfmResourceFileDir, &pwzResourceFilePath);
171 ExitOnFailure(hr, "failed to get ApplicationIdentity for Wix4PerfmonManifest");
172 size_t iResourcePath = lstrlenW(pwzResourceFilePath);
173 if ( iResourcePath > 0 && *(pwzResourceFilePath + iResourcePath -1) == L'\\')
174 *(pwzResourceFilePath + iResourcePath -1) = 0; //remove the trailing '\'
175
176 hr = StrAllocFormatted(&pwzCommand, L"\"lodctr.exe\" /m:\"%s\" \"%s\"", pwzFile, pwzResourceFilePath);
177 ExitOnFailure(hr, "failed to copy string in Wix4PerfmonManifest");
178
179 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"RollbackUnregisterPerfmonManifest"), pwzCommand, COST_PERFMONMANIFEST_REGISTER);
180 ExitOnFailure(hr, "failed to schedule RollbackUnregisterPerfmonManifest action");
181
182 hr = StrAllocFormatted(&pwzCommand, L"\"unlodctr.exe\" /m:\"%s\"", pwzFile);
183 ExitOnFailure(hr, "failed to copy string in PerfMonManifest");
184
185 WcaLog(LOGMSG_VERBOSE, "UnRegisterPerfmonManifest's CustomActionData: '%ls'", pwzCommand);
186
187 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"UnregisterPerfmonManifest"), pwzCommand, COST_PERFMONMANIFEST_UNREGISTER);
188 ExitOnFailure(hr, "failed to schedule UnregisterPerfmonManifest action");
189 }
190
191 if (hr == E_NOMOREITEMS)
192 {
193 hr = S_OK;
194 }
195 ExitOnFailure(hr, "Failure while processing PerfMonManifest");
196
197 hr = S_OK;
198
199LExit:
200 ReleaseStr(pwzData);
201 ReleaseStr(pwzResourceFilePath);
202 ReleaseStr(pwzFile);
203 ReleaseStr(pwzCommand);
204
205 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
206 return WcaFinalize(er);
207}
208
209/********************************************************************
210 ConfigureEventManifestRegister - CUSTOM ACTION ENTRY POINT for scheduling
211 Event manifest registering
212
213********************************************************************/
214extern "C" UINT __stdcall ConfigureEventManifestRegister(
215 __in MSIHANDLE hInstall
216 )
217{
218 HRESULT hr;
219 UINT er = ERROR_SUCCESS;
220
221 PMSIHANDLE hView, hRec;
222 LPWSTR pwzData = NULL, pwzFile = NULL, pwzCommand = NULL;
223 INSTALLSTATE isInstalled, isAction;
224
225 hr = WcaInitialize(hInstall, "ConfigureEventManifestReg");
226 ExitOnFailure(hr, "Failed to initialize");
227
228 if (!IsVistaOrAbove())
229 {
230 WcaLog(LOGMSG_VERBOSE, "Skipping ConfigureEventManifestRegister() because the target system does not support event manifest");
231 ExitFunction1(hr = S_FALSE);
232 }
233 // check to see if necessary tables are specified
234 if (S_OK != WcaTableExists(L"Wix4EventManifest"))
235 {
236 WcaLog(LOGMSG_VERBOSE, "Skipping ConfigureEventManifestRegister() because Wix4EventManifest table not present");
237 ExitFunction1(hr = S_FALSE);
238 }
239
240 hr = WcaOpenExecuteView(vcsEventManifestQuery, &hView);
241 ExitOnFailure(hr, "failed to open view on Wix4EventManifest table");
242 while ((hr = WcaFetchRecord(hView, &hRec)) == S_OK)
243 {
244 // get component install state
245 hr = WcaGetRecordString(hRec, emComponent, &pwzData);
246 ExitOnFailure(hr, "failed to get Component for Wix4EventManifest");
247 er = ::MsiGetComponentStateW(hInstall, pwzData, &isInstalled, &isAction);
248 hr = HRESULT_FROM_WIN32(er);
249 ExitOnFailure(hr, "failed to get Component state for Wix4EventManifest");
250 if (!WcaIsInstalling(isInstalled, isAction))
251 {
252 continue;
253 }
254
255 hr = WcaGetRecordFormattedString(hRec, emFile, &pwzFile);
256 ExitOnFailure(hr, "failed to get File for Wix4EventManifest");
257
258 hr = StrAllocFormatted(&pwzCommand, L"\"wevtutil.exe\" um \"%s\"", pwzFile);
259 ExitOnFailure(hr, "failed to copy string in Wix4EventManifest");
260
261 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"RollbackRegisterEventManifest"), pwzCommand, COST_PERFMONMANIFEST_UNREGISTER);
262 ExitOnFailure(hr, "failed to schedule RollbackRegisterEventManifest action");
263
264 hr = StrAllocFormatted(&pwzCommand, L"\"wevtutil.exe\" im \"%s\"", pwzFile);
265 ExitOnFailure(hr, "failed to copy string in Wix4EventManifest");
266 WcaLog(LOGMSG_VERBOSE, "RegisterEventManifest's CustomActionData: '%ls'", pwzCommand);
267
268 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"RegisterEventManifest"), pwzCommand, COST_EVENTMANIFEST_REGISTER);
269 ExitOnFailure(hr, "failed to schedule RegisterEventManifest action");
270 }
271
272 if (hr == E_NOMOREITEMS)
273 {
274 hr = S_OK;
275 }
276 ExitOnFailure(hr, "Failure while processing Wix4EventManifest");
277
278 hr = S_OK;
279
280LExit:
281 ReleaseStr(pwzData);
282 ReleaseStr(pwzFile);
283 ReleaseStr(pwzCommand);
284
285 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
286 return WcaFinalize(er);
287}
288
289
290
291/********************************************************************
292 ConfigureEventManifestRegister - CUSTOM ACTION ENTRY POINT for scheduling
293 Event manifest registering
294
295********************************************************************/
296extern "C" UINT __stdcall ConfigureEventManifestUnregister(
297 __in MSIHANDLE hInstall
298 )
299{
300 HRESULT hr;
301 UINT er = ERROR_SUCCESS;
302
303 PMSIHANDLE hView, hRec;
304 LPWSTR pwzData = NULL, pwzFile = NULL, pwzCommand = NULL;
305 INSTALLSTATE isInstalled, isAction;
306
307 hr = WcaInitialize(hInstall, "ConfigureEventManifestUnreg");
308 ExitOnFailure(hr, "Failed to initialize");
309
310 if (!IsVistaOrAbove())
311 {
312 WcaLog(LOGMSG_VERBOSE, "Skipping ConfigureEventManifestUnregister() because the target system does not support event manifest");
313 ExitFunction1(hr = S_FALSE);
314 }
315 // check to see if necessary tables are specified
316 if (S_OK != WcaTableExists(L"Wix4EventManifest"))
317 {
318 WcaLog(LOGMSG_VERBOSE, "Skipping ConfigureEventManifestUnregister() because Wix4EventManifest table not present");
319 ExitFunction1(hr = S_FALSE);
320 }
321
322 hr = WcaOpenExecuteView(vcsEventManifestQuery, &hView);
323 ExitOnFailure(hr, "failed to open view on Wix4EventManifest table");
324 while ((hr = WcaFetchRecord(hView, &hRec)) == S_OK)
325 {
326 // get component install state
327 hr = WcaGetRecordString(hRec, emComponent, &pwzData);
328 ExitOnFailure(hr, "failed to get Component for Wix4EventManifest");
329 er = ::MsiGetComponentStateW(hInstall, pwzData, &isInstalled, &isAction);
330 hr = HRESULT_FROM_WIN32(er);
331 ExitOnFailure(hr, "failed to get Component state for Wix4EventManifest");
332
333 // nothing to do on an install
334 // schedule the rollback action when reinstalling to re-register pre-patch manifest
335 if (!WcaIsUninstalling(isInstalled, isAction) && !WcaIsReInstalling(isInstalled, isAction))
336 {
337 continue;
338 }
339
340 hr = WcaGetRecordFormattedString(hRec, emFile, &pwzFile);
341 ExitOnFailure(hr, "failed to get File for Wix4EventManifest");
342
343 hr = StrAllocFormatted(&pwzCommand, L"\"wevtutil.exe\" im \"%s\"", pwzFile);
344 ExitOnFailure(hr, "failed to copy string in Wix4EventManifest");
345
346 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"RollbackUnregisterEventManifest"), pwzCommand, COST_PERFMONMANIFEST_REGISTER);
347 ExitOnFailure(hr, "failed to schedule RollbackUnregisterEventManifest action");
348
349 // no need to uninstall on a repair/patch. Register action will re-register and update the manifest.
350 if (!WcaIsReInstalling(isInstalled, isAction))
351 {
352 hr = StrAllocFormatted(&pwzCommand, L"\"wevtutil.exe\" um \"%s\"", pwzFile);
353 ExitOnFailure(hr, "failed to copy string in Wix4EventManifest");
354 WcaLog(LOGMSG_VERBOSE, "UnregisterEventManifest's CustomActionData: '%ls'", pwzCommand);
355
356 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"UnregisterEventManifest"), pwzCommand, COST_PERFMONMANIFEST_UNREGISTER);
357 ExitOnFailure(hr, "failed to schedule UnregisterEventManifest action");
358 }
359 }
360
361 if (hr == E_NOMOREITEMS)
362 {
363 hr = S_OK;
364 }
365 ExitOnFailure(hr, "Failure while processing Wix4EventManifest");
366
367 hr = S_OK;
368
369LExit:
370 ReleaseStr(pwzData);
371 ReleaseStr(pwzFile);
372 ReleaseStr(pwzCommand);
373
374 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
375 return WcaFinalize(er);
376}
377
diff --git a/src/ext/Util/ca/scaperf.cpp b/src/ext/Util/ca/scaperf.cpp
new file mode 100644
index 00000000..fd301278
--- /dev/null
+++ b/src/ext/Util/ca/scaperf.cpp
@@ -0,0 +1,310 @@
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
5LPCWSTR vcsPerfCounterDataQuery = L"SELECT `Wix4PerformanceCategory`, `Component_`, `Name`, `IniData`, `ConstantData` FROM `Wix4PerformanceCategory`";
6enum ePerfCounterDataQuery { pcdqId = 1, pcdqComponent, pcdqName, pcdqIniData, pcdqConstantData };
7
8LPCWSTR vcsPerfMonQuery = L"SELECT `Component_`, `File`, `Name` FROM `Wix4Perfmon`";
9enum ePerfMonQuery { pmqComponent = 1, pmqFile, pmqName };
10
11
12static HRESULT ProcessPerformanceCategory(
13 __in MSIHANDLE hInstall,
14 __in BOOL fInstall
15 );
16
17
18/********************************************************************
19 InstallPerfCounterData - CUSTOM ACTION ENTRY POINT for installing
20 Performance Counters.
21
22********************************************************************/
23extern "C" UINT __stdcall InstallPerfCounterData(
24 __in MSIHANDLE hInstall
25 )
26{
27 // AssertSz(FALSE, "debug InstallPerfCounterData{}");
28 HRESULT hr;
29 UINT er = ERROR_SUCCESS;
30
31 hr = WcaInitialize(hInstall, "InstallPerfCounterData");
32 ExitOnFailure(hr, "Failed to initialize InstallPerfCounterData.");
33
34 hr = ProcessPerformanceCategory(hInstall, TRUE);
35 MessageExitOnFailure(hr, msierrInstallPerfCounterData, "Failed to process Wix4PerformanceCategory table.");
36
37LExit:
38 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
39 return WcaFinalize(er);
40}
41
42
43/********************************************************************
44 UninstallPerfCounterData - CUSTOM ACTION ENTRY POINT for installing
45 Performance Counters.
46
47********************************************************************/
48extern "C" UINT __stdcall UninstallPerfCounterData(
49 __in MSIHANDLE hInstall
50 )
51{
52 // AssertSz(FALSE, "debug UninstallPerfCounterData{}");
53 HRESULT hr;
54 UINT er = ERROR_SUCCESS;
55
56 hr = WcaInitialize(hInstall, "UninstallPerfCounterData");
57 ExitOnFailure(hr, "Failed to initialize UninstallPerfCounterData.");
58
59 hr = ProcessPerformanceCategory(hInstall, FALSE);
60 MessageExitOnFailure(hr, msierrUninstallPerfCounterData, "Failed to process Wix4PerformanceCategory table.");
61
62LExit:
63 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
64 return WcaFinalize(er);
65}
66
67
68/********************************************************************
69 RegisterPerfmon - CUSTOM ACTION ENTRY POINT for installing Perfmon counters
70
71********************************************************************/
72extern "C" UINT __stdcall ConfigurePerfmonInstall(
73 __in MSIHANDLE hInstall
74 )
75{
76// Assert(FALSE);
77 HRESULT hr;
78 UINT er = ERROR_SUCCESS;
79
80 PMSIHANDLE hView, hRec;
81 LPWSTR pwzData = NULL, pwzName = NULL, pwzFile = NULL;
82 INSTALLSTATE isInstalled, isAction;
83
84 hr = WcaInitialize(hInstall, "ConfigurePerfmonInstall");
85 ExitOnFailure(hr, "Failed to initialize");
86
87 // check to see if necessary tables are specified
88 if (S_OK != WcaTableExists(L"Wix4Perfmon"))
89 {
90 WcaLog(LOGMSG_VERBOSE, "Skipping RegisterPerfmon() because Wix4Perfmon table not present");
91 ExitFunction1(hr = S_FALSE);
92 }
93
94 hr = WcaOpenExecuteView(vcsPerfMonQuery, &hView);
95 ExitOnFailure(hr, "failed to open view on PerfMon table");
96 while ((hr = WcaFetchRecord(hView, &hRec)) == S_OK)
97 {
98 // get component install state
99 hr = WcaGetRecordString(hRec, pmqComponent, &pwzData);
100 ExitOnFailure(hr, "failed to get Component for PerfMon");
101 er = ::MsiGetComponentStateW(hInstall, pwzData, &isInstalled, &isAction);
102 hr = HRESULT_FROM_WIN32(er);
103 ExitOnFailure(hr, "failed to get Component state for PerfMon");
104 if (!WcaIsInstalling(isInstalled, isAction))
105 {
106 continue;
107 }
108
109 hr = WcaGetRecordString(hRec, pmqName, &pwzName);
110 ExitOnFailure(hr, "failed to get Name for PerfMon");
111
112 hr = WcaGetRecordFormattedString(hRec, pmqFile, &pwzFile);
113 ExitOnFailure(hr, "failed to get File for PerfMon");
114
115 WcaLog(LOGMSG_VERBOSE, "ConfigurePerfmonInstall's CustomActionData: '%ls', '%ls'", pwzName, pwzFile);
116 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"RegisterPerfmon"), pwzFile, COST_PERFMON_REGISTER);
117 ExitOnFailure(hr, "failed to schedule RegisterPerfmon action");
118 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"RollbackRegisterPerfmon"), pwzName, COST_PERFMON_UNREGISTER);
119 ExitOnFailure(hr, "failed to schedule RollbackRegisterPerfmon action");
120 }
121
122 if (hr == E_NOMOREITEMS)
123 {
124 hr = S_OK;
125 }
126 ExitOnFailure(hr, "Failure while processing PerfMon");
127
128 hr = S_OK;
129
130LExit:
131 ReleaseStr(pwzData);
132 ReleaseStr(pwzName);
133 ReleaseStr(pwzFile);
134
135 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
136 return WcaFinalize(er);
137}
138
139
140/********************************************************************
141 ConfigurePerfmonUninstall - CUSTOM ACTION ENTRY POINT for uninstalling
142 Perfmon counters
143
144********************************************************************/
145extern "C" UINT __stdcall ConfigurePerfmonUninstall(
146 __in MSIHANDLE hInstall
147 )
148{
149// Assert(FALSE);
150 HRESULT hr;
151 UINT er = ERROR_SUCCESS;
152
153 PMSIHANDLE hView, hRec;
154 LPWSTR pwzData = NULL, pwzName = NULL, pwzFile = NULL;
155 INSTALLSTATE isInstalled, isAction;
156
157 hr = WcaInitialize(hInstall, "ConfigurePerfmonUninstall");
158 ExitOnFailure(hr, "Failed to initialize");
159
160 // check to see if necessary tables are specified
161 if (WcaTableExists(L"Wix4Perfmon") != S_OK)
162 {
163 WcaLog(LOGMSG_VERBOSE, "Skipping UnregisterPerfmon() because Wix4Perfmon table not present");
164 ExitFunction1(hr = S_FALSE);
165 }
166
167 hr = WcaOpenExecuteView(vcsPerfMonQuery, &hView);
168 ExitOnFailure(hr, "failed to open view on PerfMon table");
169 while ((hr = WcaFetchRecord(hView, &hRec)) == S_OK)
170 {
171 // get component install state
172 hr = WcaGetRecordString(hRec, pmqComponent, &pwzData);
173 ExitOnFailure(hr, "failed to get Component for PerfMon");
174 er = ::MsiGetComponentStateW(hInstall, pwzData, &isInstalled, &isAction);
175 hr = HRESULT_FROM_WIN32(er);
176 ExitOnFailure(hr, "failed to get Component state for PerfMon");
177 if (!WcaIsUninstalling(isInstalled, isAction))
178 {
179 continue;
180 }
181
182 hr = WcaGetRecordString(hRec, pmqName, &pwzName);
183 ExitOnFailure(hr, "failed to get Name for PerfMon");
184
185 hr = WcaGetRecordFormattedString(hRec, pmqFile, &pwzFile);
186 ExitOnFailure(hr, "failed to get File for PerfMon");
187
188 WcaLog(LOGMSG_VERBOSE, "ConfigurePerfmonUninstall's CustomActionData: '%ls', '%ls'", pwzName, pwzFile);
189 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"UnregisterPerfmon"), pwzName, COST_PERFMON_UNREGISTER);
190 ExitOnFailure(hr, "failed to schedule UnregisterPerfmon action");
191 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"RollbackUnregisterPerfmon"), pwzFile, COST_PERFMON_REGISTER);
192 ExitOnFailure(hr, "failed to schedule RollbackUnregisterPerfmon action");
193 }
194
195 if (hr == E_NOMOREITEMS)
196 {
197 hr = S_OK;
198 }
199 ExitOnFailure(hr, "Failure while processing PerfMon");
200
201 hr = S_OK;
202
203LExit:
204 ReleaseStr(pwzData);
205 ReleaseStr(pwzName);
206 ReleaseStr(pwzFile);
207
208 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
209 return WcaFinalize(er);
210}
211
212
213
214static HRESULT ProcessPerformanceCategory(
215 __in MSIHANDLE hInstall,
216 __in BOOL fInstall
217 )
218{
219 HRESULT hr = S_OK;
220 DWORD er = ERROR_SUCCESS;
221
222 PMSIHANDLE hView, hRec;
223 LPWSTR pwzId = NULL;
224 LPWSTR pwzComponent = NULL;
225 LPWSTR pwzName = NULL;
226 LPWSTR pwzData = NULL;
227 INSTALLSTATE isInstalled, isAction;
228
229 LPWSTR pwzCustomActionData = NULL;
230
231 // check to see if necessary tables are specified
232 if (S_OK != WcaTableExists(L"Wix4PerformanceCategory"))
233 {
234 ExitFunction1(hr = S_FALSE);
235 }
236
237 hr = WcaOpenExecuteView(vcsPerfCounterDataQuery, &hView);
238 ExitOnFailure(hr, "failed to open view on Wix4PerformanceCategory table");
239 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
240 {
241 hr = WcaGetRecordString(hRec, pcdqId, &pwzId);
242 ExitOnFailure(hr, "Failed to get id for Wix4PerformanceCategory.");
243
244 // Check to see if the Component is being installed or uninstalled
245 // when we are processing the same.
246 hr = WcaGetRecordString(hRec, pcdqComponent, &pwzComponent);
247 ExitOnFailure(hr, "Failed to get Component for Wix4PerformanceCategory: %ls", pwzId);
248
249 er = ::MsiGetComponentStateW(hInstall, pwzComponent, &isInstalled, &isAction);
250 hr = HRESULT_FROM_WIN32(er);
251 ExitOnFailure(hr, "Failed to get Component state for Wix4PerformanceCategory: %ls", pwzId);
252
253 if ((fInstall && !WcaIsInstalling(isInstalled, isAction)) ||
254 (!fInstall && !WcaIsUninstalling(isInstalled, isAction)))
255 {
256 continue;
257 }
258
259 hr = WcaGetRecordString(hRec, pcdqName, &pwzName);
260 ExitOnFailure(hr, "Failed to get Name for Wix4PerformanceCategory: %ls", pwzId);
261 hr = WcaWriteStringToCaData(pwzName, &pwzCustomActionData);
262 ExitOnFailure(hr, "Failed to add Name to CustomActionData for Wix4PerformanceCategory: %ls", pwzId);
263
264 hr = WcaGetRecordString(hRec, pcdqIniData, &pwzData);
265 ExitOnFailure(hr, "Failed to get IniData for Wix4PerformanceCategory: %ls", pwzId);
266 hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData);
267 ExitOnFailure(hr, "Failed to add IniData to CustomActionData for Wix4PerformanceCategory: %ls", pwzId);
268
269 hr = WcaGetRecordString(hRec, pcdqConstantData, &pwzData);
270 ExitOnFailure(hr, "Failed to get ConstantData for Wix4PerformanceCategory: %ls", pwzId);
271 hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData);
272 ExitOnFailure(hr, "Failed to add ConstantData to CustomActionData for Wix4PerformanceCategory: %ls", pwzId);
273 }
274
275 if (hr == E_NOMOREITEMS)
276 {
277 hr = S_OK;
278 }
279 ExitOnFailure(hr, "Failure while processing Wix4PerformanceCategory table.");
280
281 // If there was any data built up, schedule it for execution.
282 if (pwzCustomActionData)
283 {
284 if (fInstall)
285 {
286 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"RollbackRegisterPerfCounterData"), pwzCustomActionData, COST_PERFMON_UNREGISTER);
287 ExitOnFailure(hr, "Failed to schedule RollbackRegisterPerfCounterData action for Wix4PerformanceCategory: %ls", pwzId);
288
289 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"RegisterPerfCounterData"), pwzCustomActionData, COST_PERFMON_REGISTER);
290 ExitOnFailure(hr, "Failed to schedule RegisterPerfCounterData action for Wix4PerformanceCategory: %ls", pwzId);
291 }
292 else
293 {
294 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"RollbackUnregisterPerfCounterData"), pwzCustomActionData, COST_PERFMON_REGISTER);
295 ExitOnFailure(hr, "Failed to schedule RollbackUnregisterPerfCounterData action for Wix4PerformanceCategory: %ls", pwzId);
296
297 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"UnregisterPerfCounterData"), pwzCustomActionData, COST_PERFMON_UNREGISTER);
298 ExitOnFailure(hr, "Failed to schedule UnregisterPerfCounterData action for Wix4PerformanceCategory: %ls", pwzId);
299 }
300 }
301
302LExit:
303 ReleaseStr(pwzCustomActionData);
304 ReleaseStr(pwzData);
305 ReleaseStr(pwzName);
306 ReleaseStr(pwzComponent);
307 ReleaseStr(pwzId);
308
309 return hr;
310}
diff --git a/src/ext/Util/ca/scaperfexec.cpp b/src/ext/Util/ca/scaperfexec.cpp
new file mode 100644
index 00000000..c5425754
--- /dev/null
+++ b/src/ext/Util/ca/scaperfexec.cpp
@@ -0,0 +1,423 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5typedef DWORD (STDAPICALLTYPE *PFNPERFCOUNTERTEXTSTRINGS)(LPWSTR lpCommandLine, BOOL bQuietModeArg);
6
7static HRESULT ExecutePerfCounterData(
8 __in MSIHANDLE hInstall,
9 __in BOOL fInstall
10 );
11static HRESULT CreateDataFile(
12 __in LPCWSTR wzTempFolder,
13 __in LPCWSTR wzData,
14 __in BOOL fIniData,
15 __out HANDLE *phFile,
16 __out_opt LPWSTR *ppwzFile
17 );
18
19
20/********************************************************************
21 RegisterPerfCounterData - CUSTOM ACTION ENTRY POINT for registering
22 performance counters
23
24 Input: deferred CustomActionData: wzName\twzIniData\twzConstantData\twzName\twzIniData\twzConstantData\t...
25*******************************************************************/
26extern "C" UINT __stdcall RegisterPerfCounterData(
27 __in MSIHANDLE hInstall
28 )
29{
30 // AssertSz(FALSE, "debug RegisterPerfCounterData()");
31 HRESULT hr = S_OK;
32 DWORD er = ERROR_SUCCESS;
33
34 hr = WcaInitialize(hInstall, "RegisterPerfCounterData");
35 ExitOnFailure(hr, "Failed to initialize RegisterPerfCounterData.");
36
37 hr = ExecutePerfCounterData(hInstall, TRUE);
38 MessageExitOnFailure(hr, msierrInstallPerfCounterData, "Failed to execute Wix4PerformanceCategory table.");
39
40LExit:
41 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
42 return WcaFinalize(er);
43}
44
45
46/********************************************************************
47 UnregisterPerfCounterData - CUSTOM ACTION ENTRY POINT for registering
48 performance counters
49
50 Input: deferred CustomActionData: wzName\twzIniData\twzConstantData\twzName\twzIniData\twzConstantData\t...
51*******************************************************************/
52extern "C" UINT __stdcall UnregisterPerfCounterData(
53 __in MSIHANDLE hInstall
54 )
55{
56 // AssertSz(FALSE, "debug UnregisterPerfCounterData()");
57 HRESULT hr = S_OK;
58 DWORD er = ERROR_SUCCESS;
59
60 hr = WcaInitialize(hInstall, "UnregisterPerfCounterData");
61 ExitOnFailure(hr, "Failed to initialize UnregisterPerfCounterData.");
62
63 hr = ExecutePerfCounterData(hInstall, FALSE);
64 MessageExitOnFailure(hr, msierrUninstallPerfCounterData, "Failed to execute Wix4PerformanceCategory table.");
65
66LExit:
67 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
68 return WcaFinalize(er);
69}
70
71
72/********************************************************************
73 RegisterPerfmon - CUSTOM ACTION ENTRY POINT for registering
74 counters
75
76 Input: deferred CustomActionData -
77 wzFile or wzName
78*******************************************************************/
79extern "C" UINT __stdcall RegisterPerfmon(
80 __in MSIHANDLE hInstall
81 )
82{
83// Assert(FALSE);
84 UINT er = ERROR_SUCCESS;
85 HRESULT hr = S_OK;
86 LPWSTR pwzData = NULL;
87
88 HMODULE hMod = NULL;
89 PFNPERFCOUNTERTEXTSTRINGS pfnPerfCounterTextString;
90 DWORD dwRet;
91 LPWSTR pwzShortPath = NULL;
92 DWORD cchShortPath = MAX_PATH;
93 DWORD cchShortPathLength = 0;
94
95 LPWSTR pwzCommand = NULL;
96
97 hr = WcaInitialize(hInstall, "RegisterPerfmon");
98 ExitOnFailure(hr, "failed to initialize");
99
100 hr = WcaGetProperty(L"CustomActionData", &pwzData);
101 ExitOnFailure(hr, "failed to get CustomActionData");
102
103 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData);
104
105 // do the perfmon registration
106 if (NULL == hMod)
107 {
108 hr = LoadSystemLibrary(L"loadperf.dll", &hMod);
109 }
110 ExitOnFailure(hr, "failed to load DLL for PerfMon");
111
112 pfnPerfCounterTextString = (PFNPERFCOUNTERTEXTSTRINGS)::GetProcAddress(hMod, "LoadPerfCounterTextStringsW");
113 ExitOnNullWithLastError(pfnPerfCounterTextString, hr, "failed to get DLL function for PerfMon");
114
115 hr = StrAlloc(&pwzShortPath, cchShortPath);
116 ExitOnFailure(hr, "failed to allocate string");
117
118 WcaLog(LOGMSG_VERBOSE, "Converting DLL path to short format: %ls", pwzData);
119 cchShortPathLength = ::GetShortPathNameW(pwzData, pwzShortPath, cchShortPath);
120 if (cchShortPathLength > cchShortPath)
121 {
122 cchShortPath = cchShortPathLength + 1;
123 hr = StrAlloc(&pwzShortPath, cchShortPath);
124 ExitOnFailure(hr, "failed to allocate string");
125
126 cchShortPathLength = ::GetShortPathNameW(pwzData, pwzShortPath, cchShortPath);
127 }
128
129 if (0 == cchShortPathLength)
130 {
131 ExitOnLastError(hr, "failed to get short path format of path: %ls", pwzData);
132 }
133
134 hr = StrAllocFormatted(&pwzCommand, L"lodctr \"%s\"", pwzShortPath);
135 ExitOnFailure(hr, "failed to format lodctr string");
136
137 WcaLog(LOGMSG_VERBOSE, "RegisterPerfmon running command: '%ls'", pwzCommand);
138 dwRet = (*pfnPerfCounterTextString)(pwzCommand, TRUE);
139 if (dwRet != ERROR_SUCCESS && dwRet != ERROR_ALREADY_EXISTS)
140 {
141 hr = HRESULT_FROM_WIN32(dwRet);
142 MessageExitOnFailure(hr, msierrPERFMONFailedRegisterDLL, "failed to register with PerfMon, DLL: %ls", pwzData);
143 }
144
145 hr = S_OK;
146LExit:
147 ReleaseStr(pwzData);
148
149 if (FAILED(hr))
150 er = ERROR_INSTALL_FAILURE;
151 return WcaFinalize(er);
152}
153
154
155extern "C" UINT __stdcall UnregisterPerfmon(
156 __in MSIHANDLE hInstall
157 )
158{
159// Assert(FALSE);
160 UINT er = ERROR_SUCCESS;
161 HRESULT hr = S_OK;
162 LPWSTR pwzData = NULL;
163
164 HMODULE hMod = NULL;
165 PFNPERFCOUNTERTEXTSTRINGS pfnPerfCounterTextString;
166 DWORD dwRet;
167 WCHAR wz[255];
168
169 hr = WcaInitialize(hInstall, "UnregisterPerfmon");
170 ExitOnFailure(hr, "failed to initialize");
171
172 hr = WcaGetProperty(L"CustomActionData", &pwzData);
173 ExitOnFailure(hr, "failed to get CustomActionData");
174
175 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData);
176
177 // do the perfmon unregistration
178 hr = E_FAIL;
179 if (hMod == NULL)
180 {
181 hr = LoadSystemLibrary(L"loadperf.dll", &hMod);
182 }
183 ExitOnFailure(hr, "failed to load DLL for PerfMon");
184
185 pfnPerfCounterTextString = (PFNPERFCOUNTERTEXTSTRINGS)::GetProcAddress(hMod, "UnloadPerfCounterTextStringsW");
186 ExitOnNullWithLastError(pfnPerfCounterTextString, hr, "failed to get DLL function for PerfMon");
187
188 hr = ::StringCchPrintfW(wz, countof(wz), L"unlodctr \"%s\"", pwzData);
189 ExitOnFailure(hr, "Failed to format unlodctr string with: %ls", pwzData);
190 WcaLog(LOGMSG_VERBOSE, "UnregisterPerfmon running command: '%ls'", wz);
191 dwRet = (*pfnPerfCounterTextString)(wz, TRUE);
192 // if the counters aren't registered, then OK to continue
193 if (dwRet != ERROR_SUCCESS && dwRet != ERROR_FILE_NOT_FOUND && dwRet != ERROR_BADKEY)
194 {
195 hr = HRESULT_FROM_WIN32(dwRet);
196 MessageExitOnFailure(hr, msierrPERFMONFailedUnregisterDLL, "failed to unregsister with PerfMon, DLL: %ls", pwzData);
197 }
198
199 hr = S_OK;
200LExit:
201 ReleaseStr(pwzData);
202
203 if (FAILED(hr))
204 er = ERROR_INSTALL_FAILURE;
205 return WcaFinalize(er);
206}
207
208
209static HRESULT ExecutePerfCounterData(
210 __in MSIHANDLE /*hInstall*/,
211 __in BOOL fInstall
212 )
213{
214 HRESULT hr = S_OK;
215 DWORD er = ERROR_SUCCESS;
216
217 HMODULE hModule = NULL;
218 PFNPERFCOUNTERTEXTSTRINGS pfnPerfCounterTextString = NULL;
219 LPCWSTR wzPrefix = NULL;
220
221 LPWSTR pwzCustomActionData = NULL;
222 LPWSTR pwz = NULL;
223
224 LPWSTR pwzName = NULL;
225 LPWSTR pwzIniData = NULL;
226 LPWSTR pwzConstantData = NULL;
227 LPWSTR pwzTempFolder = NULL;
228 LPWSTR pwzIniFile = NULL;
229 LPWSTR pwzExecute = NULL;
230
231 HANDLE hIniData = INVALID_HANDLE_VALUE;
232 HANDLE hConstantData = INVALID_HANDLE_VALUE;
233
234 // Load the system performance counter helper DLL then get the appropriate
235 // entrypoint out of it. Fortunately, they have the same signature so we
236 // can use one function pointer to point to both.
237 hr = LoadSystemLibrary(L"loadperf.dll", &hModule);
238 ExitOnFailure(hr, "failed to load DLL for PerfMon");
239
240 if (fInstall)
241 {
242 wzPrefix = L"lodctr";
243 pfnPerfCounterTextString = (PFNPERFCOUNTERTEXTSTRINGS)::GetProcAddress(hModule, "LoadPerfCounterTextStringsW");
244 }
245 else
246 {
247 wzPrefix = L"unlodctr";
248 pfnPerfCounterTextString = (PFNPERFCOUNTERTEXTSTRINGS)::GetProcAddress(hModule, "UnloadPerfCounterTextStringsW");
249 }
250 ExitOnNullWithLastError(pfnPerfCounterTextString, hr, "Failed to get DLL function for PerfMon");
251
252 // Now get the CustomActionData and execute it.
253 hr = WcaGetProperty(L"CustomActionData", &pwzCustomActionData);
254 ExitOnFailure(hr, "Failed to get CustomActionData.");
255
256 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzCustomActionData);
257
258 pwz = pwzCustomActionData;
259
260 while (S_OK == (hr = WcaReadStringFromCaData(&pwz, &pwzName)))
261 {
262 hr = WcaReadStringFromCaData(&pwz, &pwzIniData);
263 ExitOnFailure(hr, "Failed to read IniData from custom action data.");
264
265 hr = WcaReadStringFromCaData(&pwz, &pwzConstantData);
266 ExitOnFailure(hr, "Failed to read ConstantData from custom action data.");
267
268 if (fInstall)
269 {
270 hr = PathCreateTempDirectory(NULL, L"WIXPF%03x", 999, &pwzTempFolder);
271 ExitOnFailure(hr, "Failed to create temp directory.");
272
273 hr = CreateDataFile(pwzTempFolder, pwzIniData, TRUE, &hIniData, &pwzIniFile);
274 ExitOnFailure(hr, "Failed to create .ini file for performance counter category: %ls", pwzName);
275
276 hr = CreateDataFile(pwzTempFolder, pwzConstantData, FALSE, &hConstantData, NULL);
277 ExitOnFailure(hr, "Failed to create .h file for performance counter category: %ls", pwzName);
278
279 hr = StrAllocFormatted(&pwzExecute, L"%s \"%s\"", wzPrefix, pwzIniFile);
280 ExitOnFailure(hr, "Failed to allocate string to execute.");
281
282 // Execute the install.
283 er = (*pfnPerfCounterTextString)(pwzExecute, TRUE);
284 hr = HRESULT_FROM_WIN32(er);
285 ExitOnFailure(hr, "Failed to execute install of performance counter category: %ls", pwzName);
286
287 if (INVALID_HANDLE_VALUE != hIniData)
288 {
289 ::CloseHandle(hIniData);
290 hIniData = INVALID_HANDLE_VALUE;
291 }
292
293 if (INVALID_HANDLE_VALUE != hConstantData)
294 {
295 ::CloseHandle(hConstantData);
296 hConstantData = INVALID_HANDLE_VALUE;
297 }
298
299 DirEnsureDelete(pwzTempFolder, TRUE, TRUE);
300 }
301 else
302 {
303 hr = StrAllocFormatted(&pwzExecute, L"%s \"%s\"", wzPrefix, pwzName);
304 ExitOnFailure(hr, "Failed to allocate string to execute.");
305
306 // Execute the uninstall and if the counter isn't registered then ignore
307 // the error since it won't hurt anything.
308 er = (*pfnPerfCounterTextString)(pwzExecute, TRUE);
309 if (ERROR_FILE_NOT_FOUND == er || ERROR_BADKEY == er)
310 {
311 er = ERROR_SUCCESS;
312 }
313 hr = HRESULT_FROM_WIN32(er);
314 ExitOnFailure(hr, "Failed to execute uninstall of performance counter category: %ls", pwzName);
315 }
316 }
317
318 if (E_NOMOREITEMS == hr) // If there are no more items, all is well
319 {
320 hr = S_OK;
321 }
322 ExitOnFailure(hr, "Failed to execute all perf counter data.");
323
324 hr = S_OK;
325
326LExit:
327 if (INVALID_HANDLE_VALUE != hIniData)
328 {
329 ::CloseHandle(hIniData);
330 }
331
332 if (INVALID_HANDLE_VALUE != hConstantData)
333 {
334 ::CloseHandle(hConstantData);
335 }
336
337 ReleaseStr(pwzExecute);
338 ReleaseStr(pwzIniFile);
339 ReleaseStr(pwzTempFolder);
340 ReleaseStr(pwzConstantData);
341 ReleaseStr(pwzIniData);
342 ReleaseStr(pwzName);
343 ReleaseStr(pwzCustomActionData);
344
345 if (hModule)
346 {
347 ::FreeLibrary(hModule);
348 }
349
350 return hr;
351}
352
353
354static HRESULT CreateDataFile(
355 __in LPCWSTR wzTempFolder,
356 __in LPCWSTR wzData,
357 __in BOOL fIniData,
358 __out HANDLE *phFile,
359 __out_opt LPWSTR *ppwzFile
360 )
361{
362 HRESULT hr = S_OK;
363 HANDLE hFile = INVALID_HANDLE_VALUE;
364 LPWSTR pwzFile = NULL;
365 LPSTR pszData = NULL;
366 DWORD cbData = 0;
367 DWORD cbWritten = 0;
368
369 // Convert the data to UTF-8 because lodctr/unloctr
370 // doesn't like unicode.
371 hr = StrAnsiAllocString(&pszData, wzData, 0, CP_UTF8);
372 ExitOnFailure(hr, "Failed to covert data to ANSI.");
373
374 cbData = lstrlenA(pszData);
375
376 // Concatenate the paths together, open the file data file
377 // and dump the data in there.
378 hr = StrAllocString(&pwzFile, wzTempFolder, 0);
379 ExitOnFailure(hr, "Failed to copy temp directory name.");
380
381 hr = StrAllocConcat(&pwzFile, L"wixperf", 0);
382 ExitOnFailure(hr, "Failed to add name of file.");
383
384 hr = StrAllocConcat(&pwzFile, fIniData ? L".ini" : L".h", 0);
385 ExitOnFailure(hr, "Failed to add extension of file.");
386
387 hFile = ::CreateFileW(pwzFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
388 if (INVALID_HANDLE_VALUE == hFile)
389 {
390 ExitWithLastError(hr, "Failed to open new temp file: %ls", pwzFile);
391 }
392
393 if (!::WriteFile(hFile, pszData, cbData, &cbWritten, NULL))
394 {
395 ExitWithLastError(hr, "Failed to write data to new temp file: %ls", pwzFile);
396 }
397
398 if (INVALID_HANDLE_VALUE != hFile)
399 {
400 ::CloseHandle(hFile);
401 hFile = INVALID_HANDLE_VALUE;
402 }
403
404 // Return the requested values.
405 *phFile = hFile;
406 hFile = INVALID_HANDLE_VALUE;
407
408 if (ppwzFile)
409 {
410 *ppwzFile = pwzFile;
411 pwzFile = NULL;
412 }
413
414LExit:
415 if (INVALID_HANDLE_VALUE != hFile)
416 {
417 ::CloseHandle(hFile);
418 }
419 ReleaseStr(pszData);
420 ReleaseStr(pwzFile);
421
422 return hr;
423}
diff --git a/src/ext/Util/ca/scasched.cpp b/src/ext/Util/ca/scasched.cpp
new file mode 100644
index 00000000..d81b1f14
--- /dev/null
+++ b/src/ext/Util/ca/scasched.cpp
@@ -0,0 +1,127 @@
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/********************************************************************
7ConfigureSmb - CUSTOM ACTION ENTRY POINT for installing fileshare settings
8
9********************************************************************/
10extern "C" UINT __stdcall ConfigureSmbInstall(
11 __in MSIHANDLE hInstall
12 )
13{
14 HRESULT hr = S_OK;
15 UINT er = ERROR_SUCCESS;
16
17 SCA_SMB* pssList = NULL;
18
19 // initialize
20 hr = WcaInitialize(hInstall, "ConfigureSmbInstall");
21 ExitOnFailure(hr, "Failed to initialize");
22
23 // check to see if necessary tables are specified
24 if (WcaTableExists(L"Wix4FileShare") != S_OK)
25 {
26 WcaLog(LOGMSG_VERBOSE, "Skipping SMB CustomAction, no Wix4FileShare table");
27 ExitFunction1(hr = S_FALSE);
28 }
29
30 hr = ScaSmbRead(&pssList);
31 ExitOnFailure(hr, "failed to read Wix4FileShare table");
32
33 hr = ScaSmbInstall(pssList);
34 ExitOnFailure(hr, "failed to install FileShares");
35
36LExit:
37 if (pssList)
38 ScaSmbFreeList(pssList);
39
40 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
41 return WcaFinalize(er);
42}
43
44
45/********************************************************************
46ConfigureSmb - CUSTOM ACTION ENTRY POINT for uninstalling fileshare settings
47
48********************************************************************/
49extern "C" UINT __stdcall ConfigureSmbUninstall(
50 __in MSIHANDLE hInstall
51 )
52{
53 HRESULT hr = S_OK;
54 UINT er = ERROR_SUCCESS;
55
56 SCA_SMB* pssList = NULL;
57
58 // initialize
59 hr = WcaInitialize(hInstall, "ConfigureSmbUninstall");
60 ExitOnFailure(hr, "Failed to initialize");
61
62 // check to see if necessary tables are specified
63 if (WcaTableExists(L"Wix4FileShare") != S_OK)
64 {
65 WcaLog(LOGMSG_VERBOSE, "Skipping SMB CustomAction, no Wix4FileShare table");
66 ExitFunction1(hr = S_FALSE);
67 }
68
69 hr = ScaSmbRead(&pssList);
70 ExitOnFailure(hr, "failed to read Wix4FileShare table");
71
72 hr = ScaSmbUninstall(pssList);
73 ExitOnFailure(hr, "failed to uninstall FileShares");
74
75LExit:
76 if (pssList)
77 ScaSmbFreeList(pssList);
78
79 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
80 return WcaFinalize(er);
81}
82
83
84/********************************************************************
85ConfigureUsers - CUSTOM ACTION ENTRY POINT for installing users
86
87********************************************************************/
88extern "C" UINT __stdcall ConfigureUsers(
89 __in MSIHANDLE hInstall
90 )
91{
92 //AssertSz(0, "Debug ConfigureUsers");
93
94 HRESULT hr = S_OK;
95 UINT er = ERROR_SUCCESS;
96
97 BOOL fInitializedCom = FALSE;
98 SCA_USER* psuList = NULL;
99
100 // initialize
101 hr = WcaInitialize(hInstall, "ConfigureUsers");
102 ExitOnFailure(hr, "Failed to initialize");
103
104 hr = ::CoInitialize(NULL);
105 ExitOnFailure(hr, "failed to initialize COM");
106 fInitializedCom = TRUE;
107
108 hr = ScaUserRead(&psuList);
109 ExitOnFailure(hr, "failed to read Wix4User table");
110
111 hr = ScaUserExecute(psuList);
112 ExitOnFailure(hr, "failed to add/remove User actions");
113
114LExit:
115 if (psuList)
116 {
117 ScaUserFreeList(psuList);
118 }
119
120 if (fInitializedCom)
121 {
122 ::CoUninitialize();
123 }
124
125 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
126 return WcaFinalize(er);
127} \ No newline at end of file
diff --git a/src/ext/Util/ca/scasmb.h b/src/ext/Util/ca/scasmb.h
new file mode 100644
index 00000000..f2a4b53c
--- /dev/null
+++ b/src/ext/Util/ca/scasmb.h
@@ -0,0 +1,46 @@
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 "scauser.h"
6
7// structs
8// Structure used to hold and extra user/permission pairs from the Wix4FileSharePermissions Table
9struct SCA_SMB_EX_USER_PERMS
10{
11 int nPermissions;
12 ACCESS_MODE accessMode;
13 SCA_USER scau;
14 SCA_SMB_EX_USER_PERMS* pExUserPermsNext;
15};
16
17struct SCA_SMB // hungarian ss
18{
19 WCHAR wzId[MAX_DARWIN_KEY + 1];
20 WCHAR wzShareName[MAX_DARWIN_KEY + 1];
21 WCHAR wzDescription[MAX_DARWIN_COLUMN + 1];
22 WCHAR wzComponent[MAX_DARWIN_KEY + 1];
23 WCHAR wzDirectory[MAX_PATH + 1];
24
25 int nUserPermissionCount;
26 int nPermissions;
27 SCA_SMB_EX_USER_PERMS* pExUserPerms;
28
29 INSTALLSTATE isInstalled, isAction;
30
31 BOOL fUseIntegratedAuth;
32 BOOL fLegacyUserProvided;
33 struct SCA_USER scau;
34
35 struct SCA_SMB* pssNext;
36};
37
38
39#define RESERVED 0
40
41// schedule prototypes
42HRESULT ScaSmbRead(SCA_SMB** ppssList);
43HRESULT ScaSmbExPermsRead(SCA_SMB* pss);
44HRESULT ScaSmbUninstall(SCA_SMB* pssList);
45HRESULT ScaSmbInstall(SCA_SMB* pssList);
46void ScaSmbFreeList(SCA_SMB* pssList);
diff --git a/src/ext/Util/ca/scasmbexec.cpp b/src/ext/Util/ca/scasmbexec.cpp
new file mode 100644
index 00000000..ced3aa78
--- /dev/null
+++ b/src/ext/Util/ca/scasmbexec.cpp
@@ -0,0 +1,316 @@
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 AllocateAcl - allocate an acl and populate it with this user and
8 permission information user could be user or domain\user
9
10********************************************************************/
11HRESULT AllocateAcl(SCA_SMBP* pssp, PACL* ppACL)
12{
13 HRESULT hr = S_OK;
14 EXPLICIT_ACCESSW* pEA = NULL;
15 DWORD cEA = 0;
16 DWORD dwCounter = 0;
17
18 PSID psid = NULL;
19 LPCWSTR wzUser = NULL;
20 DWORD nPermissions = 0;
21 DWORD nErrorReturn = 0;
22 ACCESS_MODE accessMode = NOT_USED_ACCESS;
23
24 cEA = pssp->dwUserPermissionCount + 1;
25 if (cEA >= MAXSIZE_T / sizeof(EXPLICIT_ACCESSW))
26 {
27 ExitOnFailure(hr = E_OUTOFMEMORY, "Too many user permissions to allocate: %u", cEA);
28 }
29
30 pEA = static_cast<EXPLICIT_ACCESSW*>(MemAlloc(cEA * sizeof(EXPLICIT_ACCESSW), TRUE));
31 ExitOnNull(pEA, hr, E_OUTOFMEMORY, "failed to allocate memory for explicit access structure");
32
33 // figure out how big the psid is
34 for (dwCounter = 0; dwCounter < pssp->dwUserPermissionCount; ++dwCounter)
35 {
36 wzUser = pssp->pUserPerms[dwCounter].wzUser;
37 nPermissions = pssp->pUserPerms[dwCounter].nPermissions;
38 accessMode = pssp->pUserPerms[dwCounter].accessMode;
39 //
40 // create the appropriate SID
41 //
42
43 // figure out the right user to put into the access block
44 if (0 == lstrcmpW(wzUser, L"Everyone"))
45 {
46 hr = AclGetWellKnownSid(WinWorldSid, &psid);
47 }
48 else if (0 == lstrcmpW(wzUser, L"Administrators"))
49 {
50 hr = AclGetWellKnownSid(WinBuiltinAdministratorsSid, &psid);
51 }
52 else if (0 == lstrcmpW(wzUser, L"LocalSystem"))
53 {
54 hr = AclGetWellKnownSid(WinLocalSystemSid, &psid);
55 }
56 else if (0 == lstrcmpW(wzUser, L"LocalService"))
57 {
58 hr = AclGetWellKnownSid(WinLocalServiceSid, &psid);
59 }
60 else if (0 == lstrcmpW(wzUser, L"NetworkService"))
61 {
62 hr = AclGetWellKnownSid(WinNetworkServiceSid, &psid);
63 }
64 else if (0 == lstrcmpW(wzUser, L"AuthenticatedUser"))
65 {
66 hr = AclGetWellKnownSid(WinAuthenticatedUserSid, &psid);
67 }
68 else if (0 == lstrcmpW(wzUser, L"Guests"))
69 {
70 hr = AclGetWellKnownSid(WinBuiltinGuestsSid, &psid);
71 }
72 else if(0 == lstrcmpW(wzUser, L"CREATOR OWNER"))
73 {
74 hr = AclGetWellKnownSid(WinCreatorOwnerSid, &psid);
75 }
76 else
77 {
78 hr = AclGetAccountSid(NULL, wzUser, &psid);
79 }
80 ExitOnFailure(hr, "failed to get sid for account: %ls", wzUser);
81
82 // we now have a valid pSid, fill in the EXPLICIT_ACCESS
83
84 /* Permissions options: (see sca.sdh for defined sdl options)
85 #define GENERIC_READ (0x80000000L) 2147483648
86 #define GENERIC_WRITE (0x40000000L) 1073741824
87 #define GENERIC_EXECUTE (0x20000000L) 536870912
88 #define GENERIC_ALL (0x10000000L) 268435456
89 */
90 pEA[dwCounter].grfAccessPermissions = nPermissions;
91 pEA[dwCounter].grfAccessMode = accessMode;
92 pEA[dwCounter].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
93#pragma prefast(push)
94#pragma prefast(disable:25029)
95 ::BuildTrusteeWithSidW(&(pEA[dwCounter].Trustee), psid);
96#pragma prefast(pop)
97 }
98
99 // create a new ACL that contains the ACE
100 *ppACL = NULL;
101#pragma prefast(push)
102#pragma prefast(disable:25029)
103 nErrorReturn = ::SetEntriesInAclW(dwCounter, pEA, NULL, ppACL);
104#pragma prefast(pop)
105 ExitOnFailure(hr = HRESULT_FROM_WIN32(nErrorReturn), "failed to allocate ACL");
106
107LExit:
108 if (psid)
109 {
110 AclFreeSid(psid);
111 }
112
113 ReleaseMem(pEA);
114
115 return hr;
116}
117
118
119
120/********************************************************************
121 FillShareInfo - fill the NetShareAdd data structure
122
123********************************************************************/
124void FillShareInfo(SHARE_INFO_502* psi, SCA_SMBP* pssp, PSECURITY_DESCRIPTOR pSD)
125{
126 psi->shi502_netname = pssp->wzKey;
127 psi->shi502_type = STYPE_DISKTREE;
128 psi->shi502_remark = pssp->wzDescription;
129 psi->shi502_permissions = 0; // not used
130 psi->shi502_max_uses = 0xFFFFFFFF;
131 psi->shi502_current_uses = 0;
132 psi->shi502_path = pssp->wzDirectory;
133 psi->shi502_passwd = NULL; // not file share perms
134 psi->shi502_reserved = 0;
135 psi->shi502_security_descriptor = pSD;
136}
137
138
139
140/* NET_API_STATUS return codes
141NERR_Success = 0
142NERR_DuplicateShare = 2118
143NERR_BufTooSmall = 2123
144NERR_NetNameNotFound = 2310
145NERR_RedirectedPath = 2117
146NERR_UnknownDevDir = 2116
147*/
148
149/********************************************************************
150 DoesShareExists - Does a share of this name exist on this computer?
151
152********************************************************************/
153HRESULT DoesShareExist(__in LPWSTR wzShareName)
154{
155 HRESULT hr = S_OK;
156 NET_API_STATUS s;
157 SHARE_INFO_502* psi = NULL;
158 s = ::NetShareGetInfo(NULL, wzShareName, 502, (BYTE**) &psi);
159
160 switch (s)
161 {
162 case NERR_Success:
163 hr = S_OK;
164 break;
165 case NERR_NetNameNotFound:
166 hr = E_FILENOTFOUND;
167 break;
168 default:
169 WcaLogError(s, "NetShareGetInfo returned an unexpected value.", NULL);
170 hr = HRESULT_FROM_WIN32(s);
171 break;
172 }
173
174 ::NetApiBufferFree(psi);
175
176 return hr;
177}
178
179
180
181/********************************************************************
182 CreateShare - create the file share on this computer
183
184********************************************************************/
185HRESULT CreateShare(SCA_SMBP* pssp)
186{
187 if (!pssp || !(pssp->wzKey))
188 return E_INVALIDARG;
189
190 HRESULT hr = S_OK;
191 PACL pACL = NULL;
192 SHARE_INFO_502 si;
193 NET_API_STATUS s;
194 DWORD dwParamErr = 0;
195
196 BOOL fShareExists = SUCCEEDED(DoesShareExist(pssp->wzKey));
197
198 PSECURITY_DESCRIPTOR pSD = static_cast<PSECURITY_DESCRIPTOR>(MemAlloc(SECURITY_DESCRIPTOR_MIN_LENGTH, TRUE));
199 ExitOnNull(pSD, hr, E_OUTOFMEMORY, "Failed to allocate memory for security descriptor");
200
201#pragma prefast(push)
202#pragma prefast(disable:25029)
203 if (!::InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION))
204#pragma prefast(pop)
205 {
206 ExitOnLastError(hr, "failed to initialize security descriptor");
207 }
208
209 hr = AllocateAcl(pssp, &pACL);
210 ExitOnFailure(hr, "Failed to allocate ACL for fileshare");
211
212 if (NULL == pACL)
213 {
214 WcaLog(LOGMSG_VERBOSE, "Ignoring NULL DACL.");
215 }
216#pragma prefast(push)
217#pragma prefast(disable:25028) // We only call this when pACL isn't NULL, so this call is safe according to the docs
218 // add the ACL to the security descriptor.
219 else if (!::SetSecurityDescriptorDacl(pSD, TRUE, pACL, FALSE))
220 {
221 ExitOnLastError(hr, "Failed to set security descriptor");
222 }
223#pragma prefast(pop)
224
225 // all that is left is to create the share
226 FillShareInfo(&si, pssp, pSD);
227
228 // Fail if the directory doesn't exist
229 if (!DirExists(pssp->wzDirectory, NULL))
230 ExitOnFailure(hr = HRESULT_FROM_WIN32(ERROR_OBJECT_NOT_FOUND), "Can't create a file share on directory that doesn't exist: %ls.", pssp->wzDirectory);
231
232 WcaLog(LOGMSG_VERBOSE, "Creating file share on directory \'%ls\' named \'%ls\'.", pssp->wzDirectory, pssp->wzKey);
233
234 if (!fShareExists)
235 {
236 s = ::NetShareAdd(NULL, 502, (BYTE*) &si, &dwParamErr);
237 WcaLog(LOGMSG_VERBOSE, "Adding a new file share.");
238 }
239 else
240 {
241 // The share exists. Write our new permissions over the top.
242 s = ::NetShareSetInfo(NULL, pssp->wzKey, 502, (BYTE*) &si, &dwParamErr);
243 WcaLog(LOGMSG_VERBOSE, "Setting permissions on existing share.");
244 }
245
246 if (NERR_Success != s)
247 {
248 hr = E_FAIL;
249 if (!fShareExists && NERR_DuplicateShare == s)
250 WcaLog(LOGMSG_VERBOSE, "Duplicate error when existence check failed.");
251
252 // error codes listed above.
253 ExitOnFailure(hr, "Failed to create/modify file share: Err: %d", s);
254 }
255
256LExit:
257 if (pACL)
258 {
259 ::LocalFree(pACL);
260 }
261
262 ReleaseMem(pSD);
263
264 return hr;
265}
266
267
268/********************************************************************
269 ScaEnsureSmbExists
270
271********************************************************************/
272HRESULT ScaEnsureSmbExists(SCA_SMBP* pssp)
273{
274 HRESULT hr = S_OK;
275
276 // create the share
277 hr = CreateShare(pssp);
278
279 return hr;
280}
281
282
283//
284// Delete File Shares - real work
285//
286
287/********************************************************************
288 ScaDropSmb - delete this file share from this computer
289
290********************************************************************/
291HRESULT ScaDropSmb(SCA_SMBP* pssp)
292{
293 HRESULT hr = S_OK;
294 NET_API_STATUS s;
295
296 hr = DoesShareExist(pssp->wzKey);
297
298 if (E_FILENOTFOUND == hr)
299 {
300 WcaLog(LOGMSG_VERBOSE, "Share doesn't exist, share removal skipped. (%ls)", pssp->wzKey);
301 ExitFunction1(hr = S_OK);
302
303 }
304
305 ExitOnFailure(hr, "Unable to detect share. (%ls)", pssp->wzKey);
306
307 s = ::NetShareDel(NULL, pssp->wzKey, 0);
308 if (NERR_Success != s)
309 {
310 hr = E_FAIL;
311 ExitOnFailure(hr, "Failed to remove file share: Err: %d", s);
312 }
313
314LExit:
315 return hr;
316}
diff --git a/src/ext/Util/ca/scasmbexec.h b/src/ext/Util/ca/scasmbexec.h
new file mode 100644
index 00000000..e3c8f8bb
--- /dev/null
+++ b/src/ext/Util/ca/scasmbexec.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
5struct SCA_SMBP_USER_PERMS
6{
7 DWORD nPermissions;
8 ACCESS_MODE accessMode;
9 WCHAR* wzUser;
10 //Not adding Password because I can't find anywhere that it is used
11};
12
13struct SCA_SMBP // hungarian ssp
14{
15 WCHAR* wzKey;
16 WCHAR* wzDescription;
17 WCHAR* wzComponent;
18 WCHAR* wzDirectory; // full path of the dir to share to
19
20 DWORD dwUserPermissionCount; //Count of SCA_SMBP_EX_USER_PERMS structures
21 SCA_SMBP_USER_PERMS* pUserPerms;
22 BOOL fUseIntegratedAuth;
23};
24
25
26HRESULT ScaEnsureSmbExists(SCA_SMBP* pssp);
27HRESULT ScaDropSmb(SCA_SMBP* pssp);
diff --git a/src/ext/Util/ca/scasmbsched.cpp b/src/ext/Util/ca/scasmbsched.cpp
new file mode 100644
index 00000000..e29f7f51
--- /dev/null
+++ b/src/ext/Util/ca/scasmbsched.cpp
@@ -0,0 +1,639 @@
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 Helper functions to maintain a list of file shares to create / remove
8
9********************************************************************/
10SCA_SMB* NewSmb()
11{
12 SCA_SMB* pss = (SCA_SMB*)MemAlloc(sizeof(SCA_SMB), TRUE);
13 Assert(pss);
14 return pss;
15}
16
17
18SCA_SMB_EX_USER_PERMS* NewExUserPermsSmb()
19{
20 SCA_SMB_EX_USER_PERMS* pExUserPerms = (SCA_SMB_EX_USER_PERMS*)MemAlloc(sizeof(SCA_SMB_EX_USER_PERMS), TRUE);
21 Assert(pExUserPerms);
22 return pExUserPerms;
23}
24
25
26SCA_SMB* AddSmbToList(SCA_SMB* pssList, SCA_SMB* pss)
27{
28 if (pssList)
29 {
30 SCA_SMB* pssT = pssList;
31 while (pssT->pssNext)
32 {
33 pssT = pssT->pssNext;
34 }
35
36 pssT->pssNext = pss;
37 }
38 else
39 {
40 pssList = pss;
41 }
42
43 return pssList;
44}
45
46
47SCA_SMB_EX_USER_PERMS* AddExUserPermsSmbToList(
48 SCA_SMB_EX_USER_PERMS* pExUserPermsList,
49 SCA_SMB_EX_USER_PERMS* pExUserPerms
50 )
51{
52 SCA_SMB_EX_USER_PERMS* pExUserPermsTemp = pExUserPermsList;
53 if (pExUserPermsList)
54 {
55 while (pExUserPermsTemp->pExUserPermsNext)
56 {
57 pExUserPermsTemp = pExUserPermsTemp->pExUserPermsNext;
58 }
59
60 pExUserPermsTemp->pExUserPermsNext = pExUserPerms;
61 }
62 else
63 {
64 pExUserPermsList = pExUserPerms;
65 }
66
67 return pExUserPermsList;
68}
69
70void ScaSmbFreeList(SCA_SMB* pssList)
71{
72 SCA_SMB* pssDelete = pssList;
73 while (pssList)
74 {
75 pssDelete = pssList;
76 pssList = pssList->pssNext;
77
78 MemFree(pssDelete);
79 }
80}
81
82void ScaExUserPermsSmbFreeList(SCA_SMB_EX_USER_PERMS* pExUserPermsList)
83{
84 SCA_SMB_EX_USER_PERMS* pExUserPermsDelete = pExUserPermsList;
85 while (pExUserPermsList)
86 {
87 pExUserPermsDelete = pExUserPermsList;
88 pExUserPermsList = pExUserPermsList->pExUserPermsNext;
89
90 MemFree(pExUserPermsDelete);
91 }
92}
93
94// sql query constants
95LPCWSTR vcsSmbQuery = L"SELECT `Wix4FileShare`, `ShareName`, `Description`, `Directory_`, "
96 L"`Component_`, `User_`, `Permissions` FROM `Wix4FileShare`";
97
98enum eSmbQuery {
99 ssqFileShare = 1,
100 ssqShareName,
101 ssqDescription,
102 ssqDirectory,
103 ssqComponent,
104 ssqUser,
105 ssqPermissions
106 };
107
108
109/********************************************************************
110 ScaSmbRead - read all of the information from the msi tables and
111 return a list of file share jobs to be done.
112
113********************************************************************/
114HRESULT ScaSmbRead(SCA_SMB** ppssList)
115{
116 HRESULT hr = S_OK;
117 UINT er = ERROR_SUCCESS;
118 PMSIHANDLE hView, hRec;
119
120 LPWSTR pwzData = NULL;
121
122 SCA_SMB* pss = NULL;
123 BOOL bUserPermissionsTableExists = FALSE;
124
125 if (S_OK != WcaTableExists(L"Wix4FileShare"))
126 {
127 WcaLog(LOGMSG_VERBOSE, "Skipping ScaSmbCreateShare() - Wix4FileShare table not present");
128 ExitFunction1(hr = S_FALSE);
129 }
130
131 if (S_OK == WcaTableExists(L"Wix4FileSharePermissions"))
132 {
133 bUserPermissionsTableExists = TRUE;
134 }
135 else
136 {
137 WcaLog(LOGMSG_VERBOSE, "No Additional Permissions - Wix4FileSharePermissions table not present");
138 }
139
140 WcaLog(LOGMSG_VERBOSE, "Reading File Share Tables");
141
142 // loop through all the fileshares
143 hr = WcaOpenExecuteView(vcsSmbQuery, &hView);
144 ExitOnFailure(hr, "Failed to open view on Wix4FileShare table");
145 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
146 {
147 pss = NewSmb();
148 if (!pss)
149 {
150 hr = E_OUTOFMEMORY;
151 break;
152 }
153 Assert(pss);
154 ::ZeroMemory(pss, sizeof(*pss));
155
156 hr = WcaGetRecordString(hRec, ssqFileShare, &pwzData);
157 ExitOnFailure(hr, "Failed to get Wix4FileShare.Wix4FileShare");
158 hr = ::StringCchCopyW(pss->wzId, countof(pss->wzId), pwzData);
159 ExitOnFailure(hr, "Failed to copy ID string to smb object");
160
161 hr = WcaGetRecordFormattedString(hRec, ssqShareName, &pwzData);
162 ExitOnFailure(hr, "Failed to get Wix4FileShare.ShareName");
163 hr = ::StringCchCopyW(pss->wzShareName, countof(pss->wzShareName), pwzData);
164 ExitOnFailure(hr, "Failed to copy share name string to smb object");
165
166 hr = WcaGetRecordString(hRec, ssqComponent, &pwzData);
167 ExitOnFailure(hr, "Failed to get Component for Wix4FileShare: '%ls'", pss->wzShareName);
168 hr = ::StringCchCopyW(pss->wzComponent, countof(pss->wzComponent), pwzData);
169 ExitOnFailure(hr, "Failed to copy component string to smb object");
170
171 hr = WcaGetRecordFormattedString(hRec, ssqDescription, &pwzData);
172 ExitOnFailure(hr, "Failed to get Share Description for Wix4FileShare: '%ls'", pss->wzShareName);
173 hr = ::StringCchCopyW(pss->wzDescription, countof(pss->wzDescription), pwzData);
174 ExitOnFailure(hr, "Failed to copy description string to smb object");
175
176 // get user info from the user table
177 hr = WcaGetRecordFormattedString(hRec, ssqUser, &pwzData);
178 ExitOnFailure(hr, "Failed to get Wix4User record for Wix4FileShare: '%ls'", pss->wzShareName);
179
180 // get component install state
181 er = ::MsiGetComponentStateW(WcaGetInstallHandle(), pss->wzComponent, &pss->isInstalled, &pss->isAction);
182 hr = HRESULT_FROM_WIN32(er);
183 ExitOnFailure(hr, "Failed to get Component state for Wix4FileShare");
184
185 // if a user was specified
186 if (*pwzData)
187 {
188 pss->fUseIntegratedAuth = FALSE;
189 pss->fLegacyUserProvided = TRUE;
190 hr = ScaGetUser(pwzData, &pss->scau);
191 ExitOnFailure(hr, "Failed to get user information for fileshare: '%ls'", pss->wzShareName);
192 }
193 else
194 {
195 pss->fLegacyUserProvided = FALSE;
196 // TODO: figure out whether this is useful still
197 //pss->fUseIntegratedAuth = TRUE;
198 // integrated authorization doesn't have a User record
199 }
200
201 // get the share's directory
202 hr = WcaGetRecordString(hRec, ssqDirectory, &pwzData);
203 ExitOnFailure(hr, "Failed to get directory for Wix4FileShare: '%ls'", pss->wzShareName);
204
205 WCHAR wzPath[MAX_PATH];
206 DWORD dwLen;
207 dwLen = countof(wzPath);
208 // review: relevant for file shares?
209 if (INSTALLSTATE_SOURCE == pss->isAction)
210 {
211 er = ::MsiGetSourcePathW(WcaGetInstallHandle(), pwzData, wzPath, &dwLen);
212 }
213 else
214 {
215 er = ::MsiGetTargetPathW(WcaGetInstallHandle(), pwzData, wzPath, &dwLen);
216 }
217 hr = HRESULT_FROM_WIN32(er);
218 ExitOnFailure(hr, "Failed to get Source/TargetPath for Directory");
219
220 // If the path is to the root of a drive, then it needs a trailing backslash.
221 // Otherwise, it can't have a trailing backslash.
222 if (3 < dwLen)
223 {
224 if (wzPath[dwLen - 1] == L'\\')
225 {
226 wzPath[dwLen - 1] = 0;
227 }
228 }
229 else if (2 == dwLen && wzPath[1] == L':')
230 {
231 wzPath[2] = L'\\';
232 wzPath[3] = 0;
233 }
234
235 hr = ::StringCchCopyW(pss->wzDirectory, countof(pss->wzDirectory), wzPath);
236 ExitOnFailure(hr, "Failed to copy directory string to smb object");
237
238 hr = WcaGetRecordInteger(hRec, ssqPermissions, &pss->nPermissions);
239 ExitOnFailure(hr, "Failed to get Wix4FileShare.Permissions");
240
241 // Check to see if additional user & permissions are specified for this share
242 if (bUserPermissionsTableExists)
243 {
244 hr = ScaSmbExPermsRead(pss);
245 ExitOnFailure(hr, "Failed to get Additional File Share Permissions");
246 }
247
248 *ppssList = AddSmbToList(*ppssList, pss);
249 pss = NULL; // set the smb NULL so it doesn't accidentally get freed below
250 }
251
252 if (E_NOMOREITEMS == hr)
253 {
254 hr = S_OK;
255 }
256 ExitOnFailure(hr, "Failure occured while processing Wix4FileShare table");
257
258LExit:
259 // if anything was left over after an error clean it all up
260 if (pss)
261 {
262 ScaSmbFreeList(pss);
263 }
264
265 ReleaseStr(pwzData);
266
267 return hr;
268}
269
270
271/********************************************************************
272 RetrieveSMBShareUserPermList - retrieve SMB Share's user permission list
273
274********************************************************************/
275HRESULT RetrieveFileShareUserPerm(SCA_SMB* pss, SCA_SMB_EX_USER_PERMS** ppExUserPermsList, DWORD *pUserPermsCount)
276{
277 HRESULT hr = S_OK;
278 SHARE_INFO_502* psi = NULL;
279 NET_API_STATUS s;
280 BOOL bValid, bDaclDefaulted;
281 PACL acl = NULL;
282 PEXPLICIT_ACCESSW pEA = NULL;
283 ULONG nCount = 0;
284 DWORD er = ERROR_SUCCESS;
285 PSID pSID = NULL;
286 DWORD nUserNameSize = MAX_DARWIN_COLUMN;
287 DWORD nDomainNameSize = MAX_DARWIN_COLUMN;
288 SID_NAME_USE peUse;
289 DWORD dwCounter = 0;
290 SCA_SMB_EX_USER_PERMS* pExUserPermsList = NULL;
291 DWORD dwUserPermsCount = 0;
292
293 *pUserPermsCount = 0;
294 s = ::NetShareGetInfo(NULL, pss->wzShareName, 502, (LPBYTE*)&psi);
295 WcaLog(LOGMSG_VERBOSE, "retrieving permissions on existing file share.");
296 if (NERR_NetNameNotFound == s)
297 {
298 WcaLog(LOGMSG_VERBOSE, "File share has already been removed.");
299 ExitFunction1(hr = S_OK);
300 }
301 else if (NERR_Success != s || psi == NULL)
302 {
303 hr = E_FAIL;
304 ExitOnFailure(hr, "Failed to get share information with return code: %d", s);
305 }
306 if (!::GetSecurityDescriptorDacl(psi->shi502_security_descriptor, &bValid, &acl, &bDaclDefaulted) || !bValid)
307 {
308 ExitOnLastError(hr, "Failed to get acl from security descriptor");
309 }
310
311 er = ::GetExplicitEntriesFromAclW(acl, &nCount, &pEA);
312 hr = HRESULT_FROM_WIN32(er);
313 ExitOnFailure(hr, "Failed to get access entries from acl for file share %ls", pss->wzShareName);
314 for (dwCounter = 0; dwCounter < nCount; ++dwCounter)
315 {
316 if (TRUSTEE_IS_SID == pEA[dwCounter].Trustee.TrusteeForm)
317 {
318 SCA_SMB_EX_USER_PERMS* pExUserPerms = NewExUserPermsSmb();
319 ::ZeroMemory(pExUserPerms, sizeof(*pExUserPerms));
320 pExUserPermsList = AddExUserPermsSmbToList(pExUserPermsList, pExUserPerms);
321 pSID = (PSID)(pEA[dwCounter].Trustee.ptstrName);
322 if (!::LookupAccountSidW(NULL, pSID, pExUserPerms->scau.wzName, &nUserNameSize, pExUserPerms->scau.wzDomain, &nDomainNameSize, &peUse))
323 {
324 hr = E_FAIL;
325 ExitOnFailure(hr, "Failed to get account name from SID");
326 }
327 pExUserPerms->nPermissions = pEA[dwCounter].grfAccessPermissions;
328 pExUserPerms->accessMode = pEA[dwCounter].grfAccessMode;
329 ++dwUserPermsCount;
330 nUserNameSize = MAX_DARWIN_COLUMN;
331 nDomainNameSize = MAX_DARWIN_COLUMN;
332 }
333 }
334 *ppExUserPermsList = pExUserPermsList;
335 *pUserPermsCount = dwUserPermsCount;
336
337LExit:
338 if (psi)
339 {
340 ::NetApiBufferFree(psi);
341 }
342
343 if (pEA)
344 {
345 ::LocalFree(pEA);
346 }
347
348 return hr;
349}
350
351
352/********************************************************************
353 SchedCreateSmb - schedule one instance of a file share creation
354
355********************************************************************/
356HRESULT SchedCreateSmb(SCA_SMB* pss)
357{
358 HRESULT hr = S_OK;
359
360 WCHAR wzDomainUser[255]; // "domain\user"
361 SCA_SMB_EX_USER_PERMS* pExUserPermsList = NULL;
362 int nCounter = 0;
363 WCHAR* pwzRollbackCustomActionData = NULL;
364 WCHAR* pwzCustomActionData = NULL;
365
366 hr = WcaWriteStringToCaData(pss->wzShareName, &pwzRollbackCustomActionData);
367 ExitOnFailure(hr, "failed to add ShareName to CustomActionData");
368
369 hr = WcaWriteStringToCaData(pss->wzShareName, &pwzCustomActionData);
370 ExitOnFailure(hr, "failed to add ShareName to CustomActionData");
371
372 hr = WcaWriteStringToCaData(pss->wzDescription, &pwzCustomActionData);
373 ExitOnFailure(hr, "Failed to add server name to CustomActionData");
374
375 hr = WcaWriteStringToCaData(pss->wzDirectory, &pwzCustomActionData);
376 ExitOnFailure(hr, "Failed to add full path instance to CustomActionData");
377
378 hr = WcaWriteStringToCaData(pss->fUseIntegratedAuth ? L"1" : L"0", &pwzCustomActionData);
379 ExitOnFailure(hr, "Failed to add server name to CustomActionData");
380
381 if (pss->fLegacyUserProvided)
382 {
383 hr = WcaWriteIntegerToCaData(pss->nUserPermissionCount + 1, &pwzCustomActionData);
384 ExitOnFailure(hr, "Failed to add additional user permission count to CustomActionData");
385
386 hr = UserBuildDomainUserName(wzDomainUser, countof(wzDomainUser), pss->scau.wzName, pss->scau.wzDomain);
387 ExitOnFailure(hr, "Failed to build user and domain name for CustomActionData");
388 hr = WcaWriteStringToCaData(wzDomainUser, &pwzCustomActionData);
389 ExitOnFailure(hr, "Failed to add server Domain\\UserName to CustomActionData");
390
391 hr = WcaWriteIntegerToCaData(pss->nPermissions, &pwzCustomActionData);
392 ExitOnFailure(hr, "Failed to add permissions to CustomActionData");
393 }
394 else
395 {
396 hr = WcaWriteIntegerToCaData(pss->nUserPermissionCount, &pwzCustomActionData);
397 ExitOnFailure(hr, "Failed to add additional user permission count to CustomActionData");
398 }
399
400 if (pss->nUserPermissionCount > 0)
401 {
402 nCounter = 0;
403 for (pExUserPermsList = pss->pExUserPerms; pExUserPermsList; pExUserPermsList = pExUserPermsList->pExUserPermsNext)
404 {
405 Assert(nCounter < pss->nUserPermissionCount);
406
407 hr = UserBuildDomainUserName(wzDomainUser, countof(wzDomainUser), pExUserPermsList->scau.wzName, pExUserPermsList->scau.wzDomain);
408 ExitOnFailure(hr, "Failed to build user and domain name for CustomActionData");
409 hr = WcaWriteStringToCaData(wzDomainUser, &pwzCustomActionData);
410 ExitOnFailure(hr, "Failed to add server Domain\\UserName to CustomActionData");
411
412 hr = WcaWriteIntegerToCaData((int)pExUserPermsList->accessMode, &pwzCustomActionData);
413 ExitOnFailure(hr, "Failed to add access mode to CustomActionData");
414
415 hr = WcaWriteIntegerToCaData(pExUserPermsList->nPermissions, &pwzCustomActionData);
416 ExitOnFailure(hr, "Failed to add permissions to CustomActionData");
417 ++nCounter;
418 }
419 Assert(nCounter == pss->nUserPermissionCount);
420 }
421
422 // Schedule the rollback first
423 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"CreateSmbRollback"), pwzRollbackCustomActionData, COST_SMB_DROPSMB);
424 ExitOnFailure(hr, "Failed to schedule DropSmb action");
425
426 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"CreateSmb"), pwzCustomActionData, COST_SMB_CREATESMB);
427 ExitOnFailure(hr, "Failed to schedule CreateSmb action");
428
429LExit:
430 ReleaseStr(pwzRollbackCustomActionData);
431 ReleaseStr(pwzCustomActionData);
432
433 if (pExUserPermsList)
434 {
435 ScaExUserPermsSmbFreeList(pExUserPermsList);
436 }
437
438 return hr;
439}
440
441
442/********************************************************************
443 ScaSmbInstall - for every file share, schedule the create custom action
444
445********************************************************************/
446HRESULT ScaSmbInstall(SCA_SMB* pssList)
447{
448 HRESULT hr = S_FALSE; // assume nothing will be done
449 SCA_SMB* pss = NULL;
450
451 for (pss = pssList; pss; pss = pss->pssNext)
452 {
453 // if installing this component
454 if (WcaIsInstalling(pss->isInstalled, pss->isAction) )
455 {
456 hr = SchedCreateSmb(pss);
457 ExitOnFailure(hr, "Failed to schedule the creation of the fileshare: %ls", pss->wzShareName);
458 }
459 }
460
461LExit:
462 return hr;
463}
464
465
466/********************************************************************
467 SchedDropSmb - schedule one instance of a file share removal
468
469********************************************************************/
470HRESULT SchedDropSmb(SCA_SMB* pss)
471{
472 HRESULT hr = S_OK;
473
474 WCHAR* pwzCustomActionData = NULL;
475 WCHAR* pwzRollbackCustomActionData = NULL;
476 SCA_SMB_EX_USER_PERMS *pExUserPermsList = NULL;
477 SCA_SMB_EX_USER_PERMS *pExUserPerm = NULL;
478 WCHAR wzDomainUser[255]; // "domain\user"
479 DWORD dwUserPermsCount = 0;
480
481 // roll back DropSmb
482 hr = WcaWriteStringToCaData(pss->wzShareName, &pwzRollbackCustomActionData);
483 ExitOnFailure(hr, "failed to add ShareName to CustomActionData");
484
485 hr = WcaWriteStringToCaData(pss->wzDescription, &pwzRollbackCustomActionData);
486 ExitOnFailure(hr, "Failed to add server name to CustomActionData");
487
488 hr = WcaWriteStringToCaData(pss->wzDirectory, &pwzRollbackCustomActionData);
489 ExitOnFailure(hr, "Failed to add full path instance to CustomActionData");
490
491 hr = WcaWriteStringToCaData(L"1", &pwzRollbackCustomActionData);
492 ExitOnFailure(hr, "Failed to add useintegrated flag to CustomActionData");
493
494 hr = RetrieveFileShareUserPerm(pss, &pExUserPermsList, &dwUserPermsCount);
495 ExitOnFailure(hr, "Failed to retrieve SMBShare's user permissions");
496
497 hr = WcaWriteIntegerToCaData((int)dwUserPermsCount, &pwzRollbackCustomActionData);
498 ExitOnFailure(hr, "Failed to add additional user permission count to CustomActionData");
499
500 for (pExUserPerm = pExUserPermsList; pExUserPerm; pExUserPerm = pExUserPerm->pExUserPermsNext)
501 {
502 hr = UserBuildDomainUserName(wzDomainUser, countof(wzDomainUser), pExUserPerm->scau.wzName, pExUserPerm->scau.wzDomain);
503 ExitOnFailure(hr, "Failed to build user and domain name for CustomActionData");
504 hr = WcaWriteStringToCaData(wzDomainUser, &pwzRollbackCustomActionData);
505 ExitOnFailure(hr, "Failed to add server Domain\\UserName to CustomActionData");
506
507 hr = WcaWriteIntegerToCaData((int)pExUserPerm->accessMode, &pwzRollbackCustomActionData);
508 ExitOnFailure(hr, "Failed to add access mode to CustomActionData");
509
510 hr = WcaWriteIntegerToCaData(pExUserPerm->nPermissions, &pwzRollbackCustomActionData);
511 ExitOnFailure(hr, "Failed to add permissions to CustomActionData");
512 }
513
514 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"DropSmbRollback"), pwzRollbackCustomActionData, COST_SMB_CREATESMB);
515 ExitOnFailure(hr, "Failed to schedule DropSmbRollback action");
516
517 // DropSMB
518 hr = WcaWriteStringToCaData(pss->wzShareName, &pwzCustomActionData);
519 ExitOnFailure(hr, "failed to add ShareName to CustomActionData");
520
521 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"DropSmb"), pwzCustomActionData, COST_SMB_DROPSMB);
522 ExitOnFailure(hr, "Failed to schedule DropSmb action");
523
524LExit:
525 ReleaseStr(pwzCustomActionData);
526
527 if (pExUserPermsList)
528 {
529 ScaExUserPermsSmbFreeList(pExUserPermsList);
530 }
531
532 return hr;
533
534}
535
536
537/********************************************************************
538 ScaSmbUninstall - for every file share, schedule the drop custom action
539
540********************************************************************/
541HRESULT ScaSmbUninstall(SCA_SMB* pssList)
542{
543 HRESULT hr = S_FALSE; // assume nothing will be done
544 SCA_SMB* pss = NULL;
545
546 for (pss = pssList; pss; pss = pss->pssNext)
547 {
548 // if uninstalling this component
549 if (WcaIsUninstalling(pss->isInstalled, pss->isAction) )
550 {
551 hr = SchedDropSmb(pss);
552 ExitOnFailure(hr, "Failed to remove file share %ls", pss->wzShareName);
553 }
554 }
555
556LExit:
557 return hr;
558}
559
560LPCWSTR vcsSmbExUserPermsQuery = L"SELECT `FileShare_`,`User_`,`Permissions` "
561 L"FROM `Wix4FileSharePermissions` WHERE `FileShare_`=?";
562
563enum eSmbUserPermsQuery {
564 ssupqFileShare = 1,
565 ssupqUser,
566 ssupqPermissions
567
568};
569
570
571/********************************************************************
572 ScaSmbExPermsRead - for Every entry in File Permissions table add a
573 User Name & Permissions structure to the List
574
575********************************************************************/
576HRESULT ScaSmbExPermsRead(SCA_SMB* pss)
577{
578 HRESULT hr = S_OK;
579 PMSIHANDLE hView, hRec;
580
581 LPWSTR pwzData = NULL;
582 SCA_SMB_EX_USER_PERMS* pExUserPermsList = pss->pExUserPerms;
583 SCA_SMB_EX_USER_PERMS* pExUserPerms = NULL;
584 int nCounter = 0;
585
586 hRec = ::MsiCreateRecord(1);
587 hr = WcaSetRecordString(hRec, 1, pss->wzId);
588 ExitOnFailure(hr, "Failed to look up FileShare");
589
590 hr = WcaOpenView(vcsSmbExUserPermsQuery, &hView);
591 ExitOnFailure(hr, "Failed to open view on Wix4FileSharePermissions table");
592 hr = WcaExecuteView(hView, hRec);
593 ExitOnFailure(hr, "Failed to execute view on Wix4FileSharePermissions table");
594
595 // loop through all User/Permissions paris returned
596 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
597 {
598 pExUserPerms = NewExUserPermsSmb();
599 if (!pExUserPerms)
600 {
601 hr = E_OUTOFMEMORY;
602 break;
603 }
604 Assert(pExUserPerms);
605 ::ZeroMemory(pExUserPerms, sizeof(*pExUserPerms));
606
607 hr = WcaGetRecordString(hRec, ssupqUser, &pwzData);
608 ExitOnFailure(hr, "Failed to get Wix4FileSharePermissions.User");
609 hr = ScaGetUser(pwzData, &pExUserPerms->scau);
610 ExitOnFailure(hr, "Failed to get user information for fileshare: '%ls'", pss->wzShareName);
611
612 hr = WcaGetRecordInteger(hRec, ssupqPermissions, &pExUserPerms->nPermissions);
613 ExitOnFailure(hr, "Failed to get Wix4FileSharePermissions.Permissions");
614 pExUserPerms->accessMode = SET_ACCESS; // we only support SET_ACCESS here
615
616 pExUserPermsList = AddExUserPermsSmbToList(pExUserPermsList, pExUserPerms);
617 ++nCounter;
618 pExUserPerms = NULL; // set the smb NULL so it doesn't accidentally get freed below
619 }
620
621 if (E_NOMOREITEMS == hr)
622 {
623 hr = S_OK;
624 pss->pExUserPerms = pExUserPermsList;
625 pss->nUserPermissionCount = nCounter;
626 }
627 ExitOnFailure(hr, "Failure occured while processing FileShare table");
628
629LExit:
630 // if anything was left over after an error clean it all up
631 if (pExUserPerms)
632 {
633 ScaExUserPermsSmbFreeList(pExUserPerms);
634 }
635
636 ReleaseStr(pwzData);
637
638 return hr;
639}
diff --git a/src/ext/Util/ca/scauser.cpp b/src/ext/Util/ca/scauser.cpp
new file mode 100644
index 00000000..b25e9daf
--- /dev/null
+++ b/src/ext/Util/ca/scauser.cpp
@@ -0,0 +1,709 @@
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
5LPCWSTR vcsUserQuery = L"SELECT `Wix4User`, `Component_`, `Name`, `Domain`, `Password` FROM `Wix4User` WHERE `Wix4User`=?";
6enum eUserQuery { vuqUser = 1, vuqComponent, vuqName, vuqDomain, vuqPassword };
7
8LPCWSTR vcsGroupQuery = L"SELECT `Wix4Group`, `Component_`, `Name`, `Domain` FROM `Wix4Group` WHERE `Wix4Group`=?";
9enum eGroupQuery { vgqGroup = 1, vgqComponent, vgqName, vgqDomain };
10
11LPCWSTR vcsUserGroupQuery = L"SELECT `Wix4User_`, `Wix4Group_` FROM `Wix4UserGroup` WHERE `Wix4User_`=?";
12enum eUserGroupQuery { vugqUser = 1, vugqGroup };
13
14LPCWSTR vActionableQuery = L"SELECT `Wix4User`,`Component_`,`Name`,`Domain`,`Password`,`Attributes` FROM `Wix4User` WHERE `Component_` IS NOT NULL";
15enum eActionableQuery { vaqUser = 1, vaqComponent, vaqName, vaqDomain, vaqPassword, vaqAttributes };
16
17
18static HRESULT AddUserToList(
19 __inout SCA_USER** ppsuList
20 );
21
22static HRESULT AddGroupToList(
23 __inout SCA_GROUP** ppsgList
24 );
25
26
27HRESULT __stdcall ScaGetUser(
28 __in LPCWSTR wzUser,
29 __out SCA_USER* pscau
30 )
31{
32 if (!wzUser || !pscau)
33 {
34 return E_INVALIDARG;
35 }
36
37 HRESULT hr = S_OK;
38 PMSIHANDLE hView, hRec;
39
40 LPWSTR pwzData = NULL;
41
42 // clear struct and bail right away if no user key was passed to search for
43 ::ZeroMemory(pscau, sizeof(*pscau));
44 if (!*wzUser)
45 {
46 ExitFunction1(hr = S_OK);
47 }
48
49 hRec = ::MsiCreateRecord(1);
50 hr = WcaSetRecordString(hRec, 1, wzUser);
51 ExitOnFailure(hr, "Failed to look up User");
52
53 hr = WcaOpenView(vcsUserQuery, &hView);
54 ExitOnFailure(hr, "Failed to open view on Wix4User table");
55 hr = WcaExecuteView(hView, hRec);
56 ExitOnFailure(hr, "Failed to execute view on Wix4User table");
57
58 hr = WcaFetchSingleRecord(hView, &hRec);
59 if (S_OK == hr)
60 {
61 hr = WcaGetRecordString(hRec, vuqUser, &pwzData);
62 ExitOnFailure(hr, "Failed to get Wix4User.User");
63 hr = ::StringCchCopyW(pscau->wzKey, countof(pscau->wzKey), pwzData);
64 ExitOnFailure(hr, "Failed to copy key string to user object");
65
66 hr = WcaGetRecordString(hRec, vuqComponent, &pwzData);
67 ExitOnFailure(hr, "Failed to get Wix4User.Component_");
68 hr = ::StringCchCopyW(pscau->wzComponent, countof(pscau->wzComponent), pwzData);
69 ExitOnFailure(hr, "Failed to copy component string to user object");
70
71 hr = WcaGetRecordFormattedString(hRec, vuqName, &pwzData);
72 ExitOnFailure(hr, "Failed to get Wix4User.Name");
73 hr = ::StringCchCopyW(pscau->wzName, countof(pscau->wzName), pwzData);
74 ExitOnFailure(hr, "Failed to copy name string to user object");
75
76 hr = WcaGetRecordFormattedString(hRec, vuqDomain, &pwzData);
77 ExitOnFailure(hr, "Failed to get Wix4User.Domain");
78 hr = ::StringCchCopyW(pscau->wzDomain, countof(pscau->wzDomain), pwzData);
79 ExitOnFailure(hr, "Failed to copy domain string to user object");
80
81 hr = WcaGetRecordFormattedString(hRec, vuqPassword, &pwzData);
82 ExitOnFailure(hr, "Failed to get Wix4User.Password");
83 hr = ::StringCchCopyW(pscau->wzPassword, countof(pscau->wzPassword), pwzData);
84 ExitOnFailure(hr, "Failed to copy password string to user object");
85 }
86 else if (E_NOMOREITEMS == hr)
87 {
88 WcaLog(LOGMSG_STANDARD, "Error: Cannot locate Wix4User.User='%ls'", wzUser);
89 hr = E_FAIL;
90 }
91 else
92 {
93 ExitOnFailure(hr, "Error or found multiple matching Wix4User rows");
94 }
95
96LExit:
97 ReleaseStr(pwzData);
98
99 return hr;
100}
101
102HRESULT __stdcall ScaGetUserDeferred(
103 __in LPCWSTR wzUser,
104 __in WCA_WRAPQUERY_HANDLE hUserQuery,
105 __out SCA_USER* pscau
106 )
107{
108 if (!wzUser || !pscau)
109 {
110 return E_INVALIDARG;
111 }
112
113 HRESULT hr = S_OK;
114 MSIHANDLE hRec, hRecTest;
115
116 LPWSTR pwzData = NULL;
117
118 // clear struct and bail right away if no user key was passed to search for
119 ::ZeroMemory(pscau, sizeof(*pscau));
120 if (!*wzUser)
121 {
122 ExitFunction1(hr = S_OK);
123 }
124
125 // Reset back to the first record
126 WcaFetchWrappedReset(hUserQuery);
127
128 hr = WcaFetchWrappedRecordWhereString(hUserQuery, vuqUser, wzUser, &hRec);
129 if (S_OK == hr)
130 {
131 hr = WcaFetchWrappedRecordWhereString(hUserQuery, vuqUser, wzUser, &hRecTest);
132 if (S_OK == hr)
133 {
134 AssertSz(FALSE, "Found multiple matching Wix4User rows");
135 }
136
137 hr = WcaGetRecordString(hRec, vuqUser, &pwzData);
138 ExitOnFailure(hr, "Failed to get Wix4User.User");
139 hr = ::StringCchCopyW(pscau->wzKey, countof(pscau->wzKey), pwzData);
140 ExitOnFailure(hr, "Failed to copy key string to user object (in deferred CA)");
141
142 hr = WcaGetRecordString(hRec, vuqComponent, &pwzData);
143 ExitOnFailure(hr, "Failed to get Wix4User.Component_");
144 hr = ::StringCchCopyW(pscau->wzComponent, countof(pscau->wzComponent), pwzData);
145 ExitOnFailure(hr, "Failed to copy component string to user object (in deferred CA)");
146
147 hr = WcaGetRecordString(hRec, vuqName, &pwzData);
148 ExitOnFailure(hr, "Failed to get Wix4User.Name");
149 hr = ::StringCchCopyW(pscau->wzName, countof(pscau->wzName), pwzData);
150 ExitOnFailure(hr, "Failed to copy name string to user object (in deferred CA)");
151
152 hr = WcaGetRecordString(hRec, vuqDomain, &pwzData);
153 ExitOnFailure(hr, "Failed to get Wix4User.Domain");
154 hr = ::StringCchCopyW(pscau->wzDomain, countof(pscau->wzDomain), pwzData);
155 ExitOnFailure(hr, "Failed to copy domain string to user object (in deferred CA)");
156
157 hr = WcaGetRecordString(hRec, vuqPassword, &pwzData);
158 ExitOnFailure(hr, "Failed to get Wix4User.Password");
159 hr = ::StringCchCopyW(pscau->wzPassword, countof(pscau->wzPassword), pwzData);
160 ExitOnFailure(hr, "Failed to copy password string to user object (in deferred CA)");
161 }
162 else if (E_NOMOREITEMS == hr)
163 {
164 WcaLog(LOGMSG_STANDARD, "Error: Cannot locate Wix4User.User='%ls'", wzUser);
165 hr = E_FAIL;
166 }
167 else
168 {
169 ExitOnFailure(hr, "Error fetching single Wix4User row");
170 }
171
172LExit:
173 ReleaseStr(pwzData);
174
175 return hr;
176}
177
178
179HRESULT __stdcall ScaGetGroup(
180 __in LPCWSTR wzGroup,
181 __out SCA_GROUP* pscag
182 )
183{
184 if (!wzGroup || !pscag)
185 {
186 return E_INVALIDARG;
187 }
188
189 HRESULT hr = S_OK;
190 PMSIHANDLE hView, hRec;
191
192 LPWSTR pwzData = NULL;
193
194 hRec = ::MsiCreateRecord(1);
195 hr = WcaSetRecordString(hRec, 1, wzGroup);
196 ExitOnFailure(hr, "Failed to look up Group");
197
198 hr = WcaOpenView(vcsGroupQuery, &hView);
199 ExitOnFailure(hr, "Failed to open view on Wix4Group table");
200 hr = WcaExecuteView(hView, hRec);
201 ExitOnFailure(hr, "Failed to execute view on Wix4Group table");
202
203 hr = WcaFetchSingleRecord(hView, &hRec);
204 if (S_OK == hr)
205 {
206 hr = WcaGetRecordString(hRec, vgqGroup, &pwzData);
207 ExitOnFailure(hr, "Failed to get Wix4Group.Wix4Group.");
208 hr = ::StringCchCopyW(pscag->wzKey, countof(pscag->wzKey), pwzData);
209 ExitOnFailure(hr, "Failed to copy Wix4Group.Wix4Group.");
210
211 hr = WcaGetRecordString(hRec, vgqComponent, &pwzData);
212 ExitOnFailure(hr, "Failed to get Wix4Group.Component_");
213 hr = ::StringCchCopyW(pscag->wzComponent, countof(pscag->wzComponent), pwzData);
214 ExitOnFailure(hr, "Failed to copy Wix4Group.Component_.");
215
216 hr = WcaGetRecordFormattedString(hRec, vgqName, &pwzData);
217 ExitOnFailure(hr, "Failed to get Wix4Group.Name");
218 hr = ::StringCchCopyW(pscag->wzName, countof(pscag->wzName), pwzData);
219 ExitOnFailure(hr, "Failed to copy Wix4Group.Name.");
220
221 hr = WcaGetRecordFormattedString(hRec, vgqDomain, &pwzData);
222 ExitOnFailure(hr, "Failed to get Wix4Group.Domain");
223 hr = ::StringCchCopyW(pscag->wzDomain, countof(pscag->wzDomain), pwzData);
224 ExitOnFailure(hr, "Failed to copy Wix4Group.Domain.");
225 }
226 else if (E_NOMOREITEMS == hr)
227 {
228 WcaLog(LOGMSG_STANDARD, "Error: Cannot locate Wix4Group.Wix4Group='%ls'", wzGroup);
229 hr = E_FAIL;
230 }
231 else
232 {
233 ExitOnFailure(hr, "Error or found multiple matching Wix4Group rows");
234 }
235
236LExit:
237 ReleaseStr(pwzData);
238
239 return hr;
240}
241
242
243void ScaUserFreeList(
244 __in SCA_USER* psuList
245 )
246{
247 SCA_USER* psuDelete = psuList;
248 while (psuList)
249 {
250 psuDelete = psuList;
251 psuList = psuList->psuNext;
252
253 ScaGroupFreeList(psuDelete->psgGroups);
254 MemFree(psuDelete);
255 }
256}
257
258
259void ScaGroupFreeList(
260 __in SCA_GROUP* psgList
261 )
262{
263 SCA_GROUP* psgDelete = psgList;
264 while (psgList)
265 {
266 psgDelete = psgList;
267 psgList = psgList->psgNext;
268
269 MemFree(psgDelete);
270 }
271}
272
273
274HRESULT ScaUserRead(
275 __out SCA_USER** ppsuList
276 )
277{
278 //Assert(FALSE);
279 Assert(ppsuList);
280
281 HRESULT hr = S_OK;
282 UINT er = ERROR_SUCCESS;
283 PMSIHANDLE hView, hRec, hUserRec, hUserGroupView;
284
285 LPWSTR pwzData = NULL;
286
287 BOOL fUserGroupExists = FALSE;
288
289 SCA_USER *psu = NULL;
290
291 INSTALLSTATE isInstalled, isAction;
292
293 if (S_OK != WcaTableExists(L"Wix4User"))
294 {
295 WcaLog(LOGMSG_VERBOSE, "Wix4User Table does not exist, exiting");
296 ExitFunction1(hr = S_FALSE);
297 }
298
299 if (S_OK == WcaTableExists(L"Wix4UserGroup"))
300 {
301 fUserGroupExists = TRUE;
302 }
303
304 //
305 // loop through all the users
306 //
307 hr = WcaOpenExecuteView(vActionableQuery, &hView);
308 ExitOnFailure(hr, "failed to open view on Wix4User table");
309 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
310 {
311 hr = WcaGetRecordString(hRec, vaqComponent, &pwzData);
312 ExitOnFailure(hr, "failed to get Wix4User.Component");
313
314 er = ::MsiGetComponentStateW(WcaGetInstallHandle(), pwzData, &isInstalled, &isAction);
315 hr = HRESULT_FROM_WIN32(er);
316 ExitOnFailure(hr, "failed to get Component state for Wix4User");
317
318 // don't bother if we aren't installing or uninstalling this component
319 if (WcaIsInstalling(isInstalled, isAction) || WcaIsUninstalling(isInstalled, isAction))
320 {
321 //
322 // Add the user to the list and populate it's values
323 //
324 hr = AddUserToList(ppsuList);
325 ExitOnFailure(hr, "failed to add user to list");
326
327 psu = *ppsuList;
328
329 psu->isInstalled = isInstalled;
330 psu->isAction = isAction;
331 hr = ::StringCchCopyW(psu->wzComponent, countof(psu->wzComponent), pwzData);
332 ExitOnFailure(hr, "failed to copy component name: %ls", pwzData);
333
334 hr = WcaGetRecordString(hRec, vaqUser, &pwzData);
335 ExitOnFailure(hr, "failed to get Wix4User.User");
336 hr = ::StringCchCopyW(psu->wzKey, countof(psu->wzKey), pwzData);
337 ExitOnFailure(hr, "failed to copy user key: %ls", pwzData);
338
339 hr = WcaGetRecordFormattedString(hRec, vaqName, &pwzData);
340 ExitOnFailure(hr, "failed to get Wix4User.Name");
341 hr = ::StringCchCopyW(psu->wzName, countof(psu->wzName), pwzData);
342 ExitOnFailure(hr, "failed to copy user name: %ls", pwzData);
343
344 hr = WcaGetRecordFormattedString(hRec, vaqDomain, &pwzData);
345 ExitOnFailure(hr, "failed to get Wix4User.Domain");
346 hr = ::StringCchCopyW(psu->wzDomain, countof(psu->wzDomain), pwzData);
347 ExitOnFailure(hr, "failed to copy user domain: %ls", pwzData);
348
349 hr = WcaGetRecordFormattedString(hRec, vaqPassword, &pwzData);
350 ExitOnFailure(hr, "failed to get Wix4User.Password");
351 hr = ::StringCchCopyW(psu->wzPassword, countof(psu->wzPassword), pwzData);
352 ExitOnFailure(hr, "failed to copy user password");
353
354 hr = WcaGetRecordInteger(hRec, vaqAttributes, &psu->iAttributes);
355 ExitOnFailure(hr, "failed to get Wix4User.Attributes");
356
357 // Check if this user is to be added to any groups
358 if (fUserGroupExists)
359 {
360 hUserRec = ::MsiCreateRecord(1);
361 hr = WcaSetRecordString(hUserRec, 1, psu->wzKey);
362 ExitOnFailure(hr, "Failed to create user record for querying Wix4UserGroup table");
363
364 hr = WcaOpenView(vcsUserGroupQuery, &hUserGroupView);
365 ExitOnFailure(hr, "Failed to open view on Wix4UserGroup table for user %ls", psu->wzKey);
366 hr = WcaExecuteView(hUserGroupView, hUserRec);
367 ExitOnFailure(hr, "Failed to execute view on Wix4UserGroup table for user: %ls", psu->wzKey);
368
369 while (S_OK == (hr = WcaFetchRecord(hUserGroupView, &hRec)))
370 {
371 hr = WcaGetRecordString(hRec, vugqGroup, &pwzData);
372 ExitOnFailure(hr, "failed to get Wix4UserGroup.Group");
373
374 hr = AddGroupToList(&(psu->psgGroups));
375 ExitOnFailure(hr, "failed to add group to list");
376
377 hr = ScaGetGroup(pwzData, psu->psgGroups);
378 ExitOnFailure(hr, "failed to get information for group: %ls", pwzData);
379 }
380
381 if (E_NOMOREITEMS == hr)
382 {
383 hr = S_OK;
384 }
385 ExitOnFailure(hr, "failed to enumerate selected rows from Wix4UserGroup table");
386 }
387 }
388 }
389
390 if (E_NOMOREITEMS == hr)
391 {
392 hr = S_OK;
393 }
394 ExitOnFailure(hr, "failed to enumerate selected rows from Wix4User table");
395
396LExit:
397 ReleaseStr(pwzData);
398
399 return hr;
400}
401
402
403static HRESULT WriteGroupInfo(
404 __in SCA_GROUP* psgList,
405 __in LPWSTR *ppwzActionData
406 )
407{
408 HRESULT hr = S_OK;
409
410 for (SCA_GROUP* psg = psgList; psg; psg = psg->psgNext)
411 {
412 hr = WcaWriteStringToCaData(psg->wzName, ppwzActionData);
413 ExitOnFailure(hr, "failed to add group name to custom action data: %ls", psg->wzName);
414
415 hr = WcaWriteStringToCaData(psg->wzDomain, ppwzActionData);
416 ExitOnFailure(hr, "failed to add group domain to custom action data: %ls", psg->wzDomain);
417 }
418
419LExit:
420 return hr;
421}
422
423
424// Behaves like WriteGroupInfo, but it filters out groups the user is currently a member of,
425// because we don't want to rollback those
426static HRESULT WriteGroupRollbackInfo(
427 __in LPCWSTR pwzName,
428 __in LPCWSTR pwzDomain,
429 __in SCA_GROUP* psgList,
430 __in LPWSTR *ppwzActionData
431 )
432{
433 HRESULT hr = S_OK;
434 BOOL fIsMember = FALSE;
435
436 for (SCA_GROUP* psg = psgList; psg; psg = psg->psgNext)
437 {
438 hr = UserCheckIsMember(pwzName, pwzDomain, psg->wzName, psg->wzDomain, &fIsMember);
439 if (FAILED(hr))
440 {
441 WcaLog(LOGMSG_VERBOSE, "Failed to check if user: %ls (domain: %ls) is member of a group while collecting rollback information (error code 0x%x) - continuing", pwzName, pwzDomain, hr);
442 hr = S_OK;
443 continue;
444 }
445
446 // If the user is currently a member, we don't want to undo that on rollback, so skip adding
447 // this group record to the list of groups to rollback
448 if (fIsMember)
449 {
450 continue;
451 }
452
453 hr = WcaWriteStringToCaData(psg->wzName, ppwzActionData);
454 ExitOnFailure(hr, "failed to add group name to custom action data: %ls", psg->wzName);
455
456 hr = WcaWriteStringToCaData(psg->wzDomain, ppwzActionData);
457 ExitOnFailure(hr, "failed to add group domain to custom action data: %ls", psg->wzDomain);
458 }
459
460LExit:
461 return hr;
462}
463
464
465/* ****************************************************************
466ScaUserExecute - Schedules user account creation or removal based on
467component state.
468
469******************************************************************/
470HRESULT ScaUserExecute(
471 __in SCA_USER *psuList
472 )
473{
474 HRESULT hr = S_OK;
475 DWORD er = 0;
476 PDOMAIN_CONTROLLER_INFOW pDomainControllerInfo = NULL;
477
478 LPWSTR pwzBaseScriptKey = NULL;
479 DWORD cScriptKey = 0;
480
481 USER_INFO_0 *pUserInfo = NULL;
482 LPWSTR pwzScriptKey = NULL;
483 LPWSTR pwzActionData = NULL;
484 LPWSTR pwzRollbackData = NULL;
485
486 // Get the base script key for this CustomAction.
487 hr = WcaCaScriptCreateKey(&pwzBaseScriptKey);
488 ExitOnFailure(hr, "Failed to get encoding key.");
489
490 // Loop through all the users to be configured.
491 for (SCA_USER *psu = psuList; psu; psu = psu->psuNext)
492 {
493 USER_EXISTS ueUserExists = USER_EXISTS_INDETERMINATE;
494
495 // Always put the User Name and Domain plus Attributes on the front of the CustomAction
496 // data. Sometimes we'll add more data.
497 Assert(psu->wzName);
498 hr = WcaWriteStringToCaData(psu->wzName, &pwzActionData);
499 ExitOnFailure(hr, "Failed to add user name to custom action data: %ls", psu->wzName);
500 hr = WcaWriteStringToCaData(psu->wzDomain, &pwzActionData);
501 ExitOnFailure(hr, "Failed to add user domain to custom action data: %ls", psu->wzDomain);
502 hr = WcaWriteIntegerToCaData(psu->iAttributes, &pwzActionData);
503 ExitOnFailure(hr, "failed to add user attributes to custom action data for user: %ls", psu->wzKey);
504
505 // Check to see if the user already exists since we have to be very careful when adding
506 // and removing users. Note: MSDN says that it is safe to call these APIs from any
507 // user, so we should be safe calling it during immediate mode.
508 er = ::NetApiBufferAllocate(sizeof(USER_INFO_0), reinterpret_cast<LPVOID*>(&pUserInfo));
509 hr = HRESULT_FROM_WIN32(er);
510 ExitOnFailure(hr, "Failed to allocate memory to check existence of user: %ls", psu->wzName);
511
512 LPCWSTR wzDomain = psu->wzDomain;
513 if (wzDomain && *wzDomain)
514 {
515 er = ::DsGetDcNameW(NULL, wzDomain, NULL, NULL, NULL, &pDomainControllerInfo);
516 if (RPC_S_SERVER_UNAVAILABLE == er)
517 {
518 // MSDN says, if we get the above error code, try again with the "DS_FORCE_REDISCOVERY" flag
519 er = ::DsGetDcNameW(NULL, wzDomain, NULL, NULL, DS_FORCE_REDISCOVERY, &pDomainControllerInfo);
520 }
521 if (ERROR_SUCCESS == er)
522 {
523 wzDomain = pDomainControllerInfo->DomainControllerName + 2; //Add 2 so that we don't get the \\ prefix
524 }
525 }
526
527 er = ::NetUserGetInfo(wzDomain, psu->wzName, 0, reinterpret_cast<LPBYTE*>(pUserInfo));
528 if (NERR_Success == er)
529 {
530 ueUserExists = USER_EXISTS_YES;
531 }
532 else if (NERR_UserNotFound == er)
533 {
534 ueUserExists = USER_EXISTS_NO;
535 }
536 else
537 {
538 ueUserExists = USER_EXISTS_INDETERMINATE;
539 hr = HRESULT_FROM_WIN32(er);
540 WcaLog(LOGMSG_VERBOSE, "Failed to check existence of domain: %ls, user: %ls (error code 0x%x) - continuing", wzDomain, psu->wzName, hr);
541 hr = S_OK;
542 er = ERROR_SUCCESS;
543 }
544
545 if (WcaIsInstalling(psu->isInstalled, psu->isAction))
546 {
547 // If the user exists, check to see if we are supposed to fail if user the exists before
548 // the install.
549 if (USER_EXISTS_YES == ueUserExists)
550 {
551 // Reinstalls will always fail if we don't remove the check for "fail if exists".
552 if (WcaIsReInstalling(psu->isInstalled, psu->isAction))
553 {
554 psu->iAttributes &= ~SCAU_FAIL_IF_EXISTS;
555 }
556
557 if ((SCAU_FAIL_IF_EXISTS & (psu->iAttributes)) && !(SCAU_UPDATE_IF_EXISTS & (psu->iAttributes)))
558 {
559 hr = HRESULT_FROM_WIN32(NERR_UserExists);
560 MessageExitOnFailure(hr, msierrUSRFailedUserCreateExists, "Failed to create user: %ls because user already exists.", psu->wzName);
561 }
562 }
563
564 // Rollback only if the user already exists, we couldn't determine if the user exists, or we are going to create the user
565 if ((USER_EXISTS_YES == ueUserExists) || (USER_EXISTS_INDETERMINATE == ueUserExists) || !(psu->iAttributes & SCAU_DONT_CREATE_USER))
566 {
567 ++cScriptKey;
568 hr = StrAllocFormatted(&pwzScriptKey, L"%ls%u", pwzBaseScriptKey, cScriptKey);
569 ExitOnFailure(hr, "Failed to create encoding key.");
570
571 // Write the script key to CustomActionData for install and rollback so information can be passed to rollback.
572 hr = WcaWriteStringToCaData(pwzScriptKey, &pwzActionData);
573 ExitOnFailure(hr, "Failed to add encoding key to custom action data.");
574
575 hr = WcaWriteStringToCaData(pwzScriptKey, &pwzRollbackData);
576 ExitOnFailure(hr, "Failed to add encoding key to rollback custom action data.");
577
578 INT iRollbackUserAttributes = psu->iAttributes;
579
580 // If the user already exists, ensure this is accounted for in rollback
581 if (USER_EXISTS_YES == ueUserExists)
582 {
583 iRollbackUserAttributes |= SCAU_DONT_CREATE_USER;
584 }
585 else
586 {
587 iRollbackUserAttributes &= ~SCAU_DONT_CREATE_USER;
588 }
589
590 // The deferred CA determines when to rollback User Rights Assignments so these should never be set.
591 iRollbackUserAttributes &= ~SCAU_ALLOW_LOGON_AS_SERVICE;
592 iRollbackUserAttributes &= ~SCAU_ALLOW_LOGON_AS_BATCH;
593
594 hr = WcaWriteStringToCaData(psu->wzName, &pwzRollbackData);
595 ExitOnFailure(hr, "Failed to add user name to rollback custom action data: %ls", psu->wzName);
596 hr = WcaWriteStringToCaData(psu->wzDomain, &pwzRollbackData);
597 ExitOnFailure(hr, "Failed to add user domain to rollback custom action data: %ls", psu->wzDomain);
598 hr = WcaWriteIntegerToCaData(iRollbackUserAttributes, &pwzRollbackData);
599 ExitOnFailure(hr, "failed to add user attributes to rollback custom action data for user: %ls", psu->wzKey);
600
601 // If the user already exists, add relevant group information to rollback data
602 if (USER_EXISTS_YES == ueUserExists || USER_EXISTS_INDETERMINATE == ueUserExists)
603 {
604 hr = WriteGroupRollbackInfo(psu->wzName, psu->wzDomain, psu->psgGroups, &pwzRollbackData);
605 ExitOnFailure(hr, "failed to add group information to rollback custom action data");
606 }
607
608 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"CreateUserRollback"), pwzRollbackData, COST_USER_DELETE);
609 ExitOnFailure(hr, "failed to schedule CreateUserRollback");
610 }
611 else
612 {
613 // Write empty script key to CustomActionData since there is no rollback.
614 hr = WcaWriteStringToCaData(L"", &pwzActionData);
615 ExitOnFailure(hr, "Failed to add empty encoding key to custom action data.");
616 }
617
618 //
619 // Schedule the creation now.
620 //
621 hr = WcaWriteStringToCaData(psu->wzPassword, &pwzActionData);
622 ExitOnFailure(hr, "failed to add user password to custom action data for user: %ls", psu->wzKey);
623
624 // Add user's group information to custom action data
625 hr = WriteGroupInfo(psu->psgGroups, &pwzActionData);
626 ExitOnFailure(hr, "failed to add group information to custom action data");
627
628 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"CreateUser"), pwzActionData, COST_USER_ADD);
629 ExitOnFailure(hr, "failed to schedule CreateUser");
630 }
631 else if (((USER_EXISTS_YES == ueUserExists) || (USER_EXISTS_INDETERMINATE == ueUserExists)) && WcaIsUninstalling(psu->isInstalled, psu->isAction) && !(psu->iAttributes & SCAU_DONT_REMOVE_ON_UNINSTALL))
632 {
633 // Add user's group information - this will ensure the user can be removed from any groups they were added to, if the user isn't be deleted
634 hr = WriteGroupInfo(psu->psgGroups, &pwzActionData);
635 ExitOnFailure(hr, "failed to add group information to custom action data");
636
637 //
638 // Schedule the removal because the user exists and we don't have any flags set
639 // that say, don't remove the user on uninstall.
640 //
641 // Note: We can't rollback the removal of a user which is why RemoveUser is a commit
642 // CustomAction.
643 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"RemoveUser"), pwzActionData, COST_USER_DELETE);
644 ExitOnFailure(hr, "failed to schedule RemoveUser");
645 }
646
647 ReleaseNullStr(pwzScriptKey);
648 ReleaseNullStr(pwzActionData);
649 ReleaseNullStr(pwzRollbackData);
650 if (pUserInfo)
651 {
652 ::NetApiBufferFree(static_cast<LPVOID>(pUserInfo));
653 pUserInfo = NULL;
654 }
655 if (pDomainControllerInfo)
656 {
657 ::NetApiBufferFree(static_cast<LPVOID>(pDomainControllerInfo));
658 pDomainControllerInfo = NULL;
659 }
660 }
661
662LExit:
663 ReleaseStr(pwzBaseScriptKey);
664 ReleaseStr(pwzScriptKey);
665 ReleaseStr(pwzActionData);
666 ReleaseStr(pwzRollbackData);
667 if (pUserInfo)
668 {
669 ::NetApiBufferFree(static_cast<LPVOID>(pUserInfo));
670 }
671 if (pDomainControllerInfo)
672 {
673 ::NetApiBufferFree(static_cast<LPVOID>(pDomainControllerInfo));
674 }
675
676 return hr;
677}
678
679
680static HRESULT AddUserToList(
681 __inout SCA_USER** ppsuList
682 )
683{
684 HRESULT hr = S_OK;
685 SCA_USER* psu = static_cast<SCA_USER*>(MemAlloc(sizeof(SCA_USER), TRUE));
686 ExitOnNull(psu, hr, E_OUTOFMEMORY, "failed to allocate memory for new user list element");
687
688 psu->psuNext = *ppsuList;
689 *ppsuList = psu;
690
691LExit:
692 return hr;
693}
694
695
696static HRESULT AddGroupToList(
697 __inout SCA_GROUP** ppsgList
698 )
699{
700 HRESULT hr = S_OK;
701 SCA_GROUP* psg = static_cast<SCA_GROUP*>(MemAlloc(sizeof(SCA_GROUP), TRUE));
702 ExitOnNull(psg, hr, E_OUTOFMEMORY, "failed to allocate memory for new group list element");
703
704 psg->psgNext = *ppsgList;
705 *ppsgList = psg;
706
707LExit:
708 return hr;
709}
diff --git a/src/ext/Util/ca/scauser.h b/src/ext/Util/ca/scauser.h
new file mode 100644
index 00000000..a5fd5ea8
--- /dev/null
+++ b/src/ext/Util/ca/scauser.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
5enum USER_EXISTS
6{
7 USER_EXISTS_YES,
8 USER_EXISTS_NO,
9 USER_EXISTS_INDETERMINATE
10};
11
12// structs
13struct SCA_GROUP
14{
15 WCHAR wzKey[MAX_DARWIN_KEY + 1];
16 WCHAR wzComponent[MAX_DARWIN_KEY + 1];
17
18 WCHAR wzDomain[MAX_DARWIN_COLUMN + 1];
19 WCHAR wzName[MAX_DARWIN_COLUMN + 1];
20
21 SCA_GROUP *psgNext;
22};
23
24struct SCA_USER
25{
26 WCHAR wzKey[MAX_DARWIN_KEY + 1];
27 WCHAR wzComponent[MAX_DARWIN_KEY + 1];
28 INSTALLSTATE isInstalled;
29 INSTALLSTATE isAction;
30
31 WCHAR wzDomain[MAX_DARWIN_COLUMN + 1];
32 WCHAR wzName[MAX_DARWIN_COLUMN + 1];
33 WCHAR wzPassword[MAX_DARWIN_COLUMN + 1];
34 INT iAttributes;
35
36 SCA_GROUP *psgGroups;
37
38 SCA_USER *psuNext;
39};
40
41
42// prototypes
43HRESULT __stdcall ScaGetUser(
44 __in LPCWSTR wzUser,
45 __out SCA_USER* pscau
46 );
47HRESULT __stdcall ScaGetUserDeferred(
48 __in LPCWSTR wzUser,
49 __in WCA_WRAPQUERY_HANDLE hUserQuery,
50 __out SCA_USER* pscau
51 );
52HRESULT __stdcall ScaGetGroup(
53 __in LPCWSTR wzGroup,
54 __out SCA_GROUP* pscag
55 );
56void ScaUserFreeList(
57 __in SCA_USER* psuList
58 );
59void ScaGroupFreeList(
60 __in SCA_GROUP* psgList
61 );
62HRESULT ScaUserRead(
63 __inout SCA_USER** ppsuList
64 );
65HRESULT ScaUserExecute(
66 __in SCA_USER *psuList
67 );
diff --git a/src/ext/Util/ca/secureobj.cpp b/src/ext/Util/ca/secureobj.cpp
new file mode 100644
index 00000000..72842eb5
--- /dev/null
+++ b/src/ext/Util/ca/secureobj.cpp
@@ -0,0 +1,915 @@
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// structs
6LPCWSTR wzQUERY_SECUREOBJECTS = L"SELECT `Wix4SecureObject`.`Wix4SecureObject`, `Wix4SecureObject`.`Table`, `Wix4SecureObject`.`Domain`, `Wix4SecureObject`.`User`, `Wix4SecureObject`.`Attributes`, "
7 L"`Wix4SecureObject`.`Permission`, `Wix4SecureObject`.`Component_`, `Component`.`Attributes` FROM `Wix4SecureObject`,`Component` WHERE "
8 L"`Wix4SecureObject`.`Component_`=`Component`.`Component`";
9enum eQUERY_SECUREOBJECTS { QSO_SECUREOBJECT = 1, QSO_TABLE, QSO_DOMAIN, QSO_USER, QSO_ATTRIBUTES, QSO_PERMISSION, QSO_COMPONENT, QSO_COMPATTRIBUTES };
10
11LPCWSTR wzQUERY_REGISTRY = L"SELECT `Registry`.`Registry`, `Registry`.`Root`, `Registry`.`Key` FROM `Registry` WHERE `Registry`.`Registry`=?";
12enum eQUERY_OBJECTCOMPONENT { QSOC_REGISTRY = 1, QSOC_REGROOT, QSOC_REGKEY };
13
14LPCWSTR wzQUERY_SERVICEINSTALL = L"SELECT `ServiceInstall`.`Name` FROM `ServiceInstall` WHERE `ServiceInstall`.`ServiceInstall`=?";
15enum eQUERY_SECURESERVICEINSTALL { QSSI_NAME = 1 };
16
17enum eOBJECTTYPE { OT_UNKNOWN, OT_SERVICE, OT_FOLDER, OT_FILE, OT_REGISTRY };
18
19enum eSECURE_OBJECT_ATTRIBUTE
20{
21 SECURE_OBJECT_ATTRIBUTE_INHERITABLE = 0x1,
22};
23
24static eOBJECTTYPE EObjectTypeFromString(
25 __in LPCWSTR pwzTable
26 )
27{
28 if (NULL == pwzTable)
29 {
30 return OT_UNKNOWN;
31 }
32
33 eOBJECTTYPE eType = OT_UNKNOWN;
34
35 // ensure we're looking at a known table
36 if (0 == lstrcmpW(L"ServiceInstall", pwzTable))
37 {
38 eType = OT_SERVICE;
39 }
40 else if (0 == lstrcmpW(L"CreateFolder", pwzTable))
41 {
42 eType = OT_FOLDER;
43 }
44 else if (0 == lstrcmpW(L"File", pwzTable))
45 {
46 eType = OT_FILE;
47 }
48 else if (0 == lstrcmpW(L"Registry", pwzTable))
49 {
50 eType = OT_REGISTRY;
51 }
52
53 return eType;
54}
55
56static SE_OBJECT_TYPE SEObjectTypeFromString(
57 __in LPCWSTR pwzTable
58 )
59{
60 if (NULL == pwzTable)
61 {
62 return SE_UNKNOWN_OBJECT_TYPE;
63 }
64
65 SE_OBJECT_TYPE objectType = SE_UNKNOWN_OBJECT_TYPE;
66
67 if (0 == lstrcmpW(L"ServiceInstall", pwzTable))
68 {
69 objectType = SE_SERVICE;
70 }
71 else if (0 == lstrcmpW(L"CreateFolder", pwzTable) || 0 == lstrcmpW(L"File", pwzTable))
72 {
73 objectType = SE_FILE_OBJECT;
74 }
75 else if (0 == lstrcmpW(L"Registry", pwzTable))
76 {
77 objectType = SE_REGISTRY_KEY;
78 }
79 else
80 {
81 // Do nothing; we'll return SE_UNKNOWN_OBJECT_TYPE, and the caller should handle the situation
82 }
83
84 return objectType;
85}
86
87static HRESULT StoreACLRollbackInfo(
88 __in LPWSTR pwzObject,
89 __in LPCWSTR pwzTable
90 )
91{
92 HRESULT hr = S_OK;
93 DWORD er = ERROR_SUCCESS;
94 PSECURITY_DESCRIPTOR psd = NULL;
95 SECURITY_DESCRIPTOR_CONTROL sdc = {0};
96 DWORD dwRevision = 0;
97 LPWSTR pwzCustomActionData = NULL;
98 LPWSTR pwzSecurityInfo = NULL;
99
100 Assert(pwzObject && pwzTable);
101
102 SE_OBJECT_TYPE objectType = SEObjectTypeFromString(const_cast<LPCWSTR> (pwzTable));
103
104 if (SE_UNKNOWN_OBJECT_TYPE != objectType)
105 {
106 er = ::GetNamedSecurityInfoW(pwzObject, objectType, DACL_SECURITY_INFORMATION, NULL, NULL, NULL, NULL, &psd);
107 if (ERROR_FILE_NOT_FOUND == er || ERROR_PATH_NOT_FOUND == er || ERROR_SERVICE_DOES_NOT_EXIST == HRESULT_CODE(er))
108 {
109 // If the file, path or service doesn't exist yet, skip rollback without a message
110 hr = HRESULT_FROM_WIN32(er);
111 ExitFunction();
112 }
113
114 ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Unable to schedule rollback for object: %ls", pwzObject);
115
116 //Need to see if DACL is protected so getting Descriptor information
117 if (!::GetSecurityDescriptorControl(psd, &sdc, &dwRevision))
118 {
119 ExitOnLastError(hr, "Unable to schedule rollback for object (failed to get security descriptor control): %ls", pwzObject);
120 }
121
122 // Convert the security information to a string, and write this to the custom action data
123 if (!::ConvertSecurityDescriptorToStringSecurityDescriptorW(psd,SDDL_REVISION_1,DACL_SECURITY_INFORMATION,&pwzSecurityInfo,NULL))
124 {
125 hr = E_UNEXPECTED;
126 ExitOnFailure(hr, "Unable to schedule rollback for object (failed to convert security descriptor to a valid security descriptor string): %ls", pwzObject);
127 }
128
129 hr = WcaWriteStringToCaData(pwzObject, &pwzCustomActionData);
130 ExitOnFailure(hr, "failed to add object data to rollback CustomActionData");
131
132 hr = WcaWriteStringToCaData(pwzTable, &pwzCustomActionData);
133 ExitOnFailure(hr, "failed to add table name to rollback CustomActionData");
134
135 hr = WcaWriteStringToCaData(pwzSecurityInfo, &pwzCustomActionData);
136 ExitOnFailure(hr, "failed to add security info data to rollback CustomActionData");
137
138 // Write a 1 if DACL is protected, 0 otherwise
139 if (sdc & SE_DACL_PROTECTED)
140 {
141 hr = WcaWriteIntegerToCaData(1,&pwzCustomActionData);
142 ExitOnFailure(hr, "failed to add data to rollbackCustomActionData");
143 }
144 else
145 {
146 hr = WcaWriteIntegerToCaData(0,&pwzCustomActionData);
147 ExitOnFailure(hr, "failed to add data to rollback CustomActionData");
148 }
149
150 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"ExecSecureObjectsRollback"), pwzCustomActionData, COST_SECUREOBJECT);
151 ExitOnFailure(hr, "failed to schedule ExecSecureObjectsRollback for item: %ls of type: %ls", pwzObject, pwzTable);
152
153 ReleaseStr(pwzCustomActionData);
154 pwzCustomActionData = NULL;
155
156 }
157 else
158 {
159 MessageExitOnFailure(hr = E_UNEXPECTED, msierrSecureObjectsUnknownType, "unknown object type: %ls", pwzTable);
160 }
161LExit:
162 ReleaseStr(pwzCustomActionData);
163
164 if (psd)
165 {
166 ::LocalFree(psd);
167 }
168
169 return hr;
170}
171
172static HRESULT GetTargetPath(
173 __in eOBJECTTYPE eType,
174 __in LPCWSTR pwzSecureObject,
175 __out LPWSTR* ppwzTargetPath
176 )
177{
178 HRESULT hr = S_OK;
179
180 PMSIHANDLE hView = NULL;
181 PMSIHANDLE hRecObject = NULL;
182 PMSIHANDLE hRec = NULL;
183
184 int iRoot = 0;
185 int iAllUsers = 0;
186 LPWSTR pwzKey = NULL;
187 LPWSTR pwzFormattedString = NULL;
188
189 if (OT_SERVICE == eType)
190 {
191 hr = WcaTableExists(L"ServiceInstall");
192 if (S_FALSE == hr)
193 {
194 hr = E_UNEXPECTED;
195 }
196 ExitOnFailure(hr, "failed to open ServiceInstall table to secure object");
197
198 hr = WcaOpenView(wzQUERY_SERVICEINSTALL, &hView);
199 ExitOnFailure(hr, "failed to open view on ServiceInstall table");
200
201 // create a record that stores the object to secure
202 hRec = MsiCreateRecord(1);
203 MsiRecordSetStringW(hRec, 1, pwzSecureObject);
204
205 // execute a view looking for the object's ServiceInstall.ServiceInstall row.
206 hr = WcaExecuteView(hView, hRec);
207 ExitOnFailure(hr, "failed to execute view on ServiceInstall table");
208 hr = WcaFetchSingleRecord(hView, &hRecObject);
209 ExitOnFailure(hr, "failed to fetch ServiceInstall row for secure object");
210
211 hr = WcaGetRecordFormattedString(hRecObject, QSSI_NAME, ppwzTargetPath);
212 ExitOnFailure(hr, "failed to get service name for secure object: %ls", pwzSecureObject);
213 }
214 else if (OT_FOLDER == eType)
215 {
216 hr = WcaGetTargetPath(pwzSecureObject, ppwzTargetPath);
217 ExitOnFailure(hr, "failed to get target path for directory id: %ls", pwzSecureObject);
218 }
219 else if (OT_FILE == eType)
220 {
221 hr = StrAllocFormatted(&pwzFormattedString, L"[#%s]", pwzSecureObject);
222 ExitOnFailure(hr, "failed to create formatted string for securing file object: %ls", pwzSecureObject);
223
224 hr = WcaGetFormattedString(pwzFormattedString, ppwzTargetPath);
225 ExitOnFailure(hr, "failed to get file path from formatted string: %ls for secure object: %ls", pwzFormattedString, pwzSecureObject);
226 }
227 else if (OT_REGISTRY == eType)
228 {
229 hr = WcaTableExists(L"Registry");
230 if (S_FALSE == hr)
231 {
232 hr = E_UNEXPECTED;
233 }
234 ExitOnFailure(hr, "failed to open Registry table to secure object");
235
236 hr = WcaOpenView(wzQUERY_REGISTRY, &hView);
237 ExitOnFailure(hr, "failed to open view on Registry table");
238
239 // create a record that stores the object to secure
240 hRec = MsiCreateRecord(1);
241 MsiRecordSetStringW(hRec, 1, pwzSecureObject);
242
243 // execute a view looking for the object's Registry row
244 hr = WcaExecuteView(hView, hRec);
245 ExitOnFailure(hr, "failed to execute view on Registry table");
246 hr = WcaFetchSingleRecord(hView, &hRecObject);
247 ExitOnFailure(hr, "failed to fetch Registry row for secure object");
248
249 hr = WcaGetRecordInteger(hRecObject, QSOC_REGROOT, &iRoot);
250 ExitOnFailure(hr, "Failed to get reg key root for secure object: %ls", pwzSecureObject);
251
252 hr = WcaGetRecordFormattedString(hRecObject, QSOC_REGKEY, &pwzKey);
253 ExitOnFailure(hr, "Failed to get reg key for secure object: %ls", pwzSecureObject);
254
255 // Decode the root value
256 if (-1 == iRoot)
257 {
258 // They didn't specify a root so that means it's either HKCU or HKLM depending on ALLUSERS property
259 hr = WcaGetIntProperty(L"ALLUSERS", &iAllUsers);
260 ExitOnFailure(hr, "failed to get value of ALLUSERS property");
261
262 if (1 == iAllUsers)
263 {
264 hr = StrAllocString(ppwzTargetPath, L"MACHINE\\", 0);
265 ExitOnFailure(hr, "failed to allocate target registry string with HKLM root");
266 }
267 else
268 {
269 hr = StrAllocString(ppwzTargetPath, L"CURRENT_USER\\", 0);
270 ExitOnFailure(hr, "failed to allocate target registry string with HKCU root");
271 }
272 }
273 else if (msidbRegistryRootClassesRoot == iRoot)
274 {
275 hr = StrAllocString(ppwzTargetPath, L"CLASSES_ROOT\\", 0);
276 ExitOnFailure(hr, "failed to allocate target registry string with HKCR root");
277 }
278 else if (msidbRegistryRootCurrentUser == iRoot)
279 {
280 hr = StrAllocString(ppwzTargetPath, L"CURRENT_USER\\", 0);
281 ExitOnFailure(hr, "failed to allocate target registry string with HKCU root");
282 }
283 else if (msidbRegistryRootLocalMachine == iRoot)
284 {
285 hr = StrAllocString(ppwzTargetPath, L"MACHINE\\", 0);
286 ExitOnFailure(hr, "failed to allocate target registry string with HKLM root");
287 }
288 else if (msidbRegistryRootUsers == iRoot)
289 {
290 hr = StrAllocString(ppwzTargetPath, L"USERS\\", 0);
291 ExitOnFailure(hr, "failed to allocate target registry string with HKU root");
292 }
293 else
294 {
295 ExitOnFailure(hr = E_UNEXPECTED, "Unknown registry key root specified for secure object: '%ls' root: %d", pwzSecureObject, iRoot);
296 }
297
298 hr = StrAllocConcat(ppwzTargetPath, pwzKey, 0);
299 ExitOnFailure(hr, "Failed to concat key: %ls for secure object: %ls", pwzKey, pwzSecureObject);
300 }
301 else
302 {
303 AssertSz(FALSE, "How did you get here?");
304 ExitOnFailure(hr = E_UNEXPECTED, "Unknown secure object type: %d", eType);
305 }
306
307LExit:
308 ReleaseStr(pwzFormattedString);
309 ReleaseStr(pwzKey);
310
311 return hr;
312}
313
314/******************************************************************
315 SchedSecureObjects - entry point for SchedSecureObjects Custom Action
316
317 called as Type 1 CustomAction (binary DLL) from Windows Installer
318 in InstallExecuteSequence, to schedule ExecSecureObjects
319******************************************************************/
320extern "C" UINT __stdcall SchedSecureObjects(
321 __in MSIHANDLE hInstall
322 )
323{
324// AssertSz(FALSE, "debug SchedSecureObjects");
325 HRESULT hr = S_OK;
326 UINT er = ERROR_SUCCESS;
327
328 LPWSTR pwzSecureObject = NULL;
329 LPWSTR pwzData = NULL;
330 LPWSTR pwzTable = NULL;
331 LPWSTR pwzTargetPath = NULL;
332
333 PMSIHANDLE hView = NULL;
334 PMSIHANDLE hRec = NULL;
335
336 INSTALLSTATE isInstalled;
337 INSTALLSTATE isAction;
338
339 LPWSTR pwzCustomActionData = NULL;
340
341 DWORD cObjects = 0;
342 eOBJECTTYPE eType = OT_UNKNOWN;
343 DWORD dwAttributes = 0;
344
345 //
346 // initialize
347 //
348 hr = WcaInitialize(hInstall, "SchedSecureObjects");
349 ExitOnFailure(hr, "failed to initialize");
350
351 // anything to do?
352 if (S_OK != WcaTableExists(L"Wix4SecureObject"))
353 {
354 WcaLog(LOGMSG_STANDARD, "Wix4SecureObject table doesn't exist, so there are no objects to secure.");
355 ExitFunction();
356 }
357
358 //
359 // loop through all the objects to be secured
360 //
361 hr = WcaOpenExecuteView(wzQUERY_SECUREOBJECTS, &hView);
362 ExitOnFailure(hr, "failed to open view on Wix4SecureObject table");
363 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
364 {
365 hr = WcaGetRecordString(hRec, QSO_TABLE, &pwzTable);
366 ExitOnFailure(hr, "failed to get object table");
367
368 eType = EObjectTypeFromString(pwzTable);
369
370 if (OT_UNKNOWN == eType)
371 {
372 ExitOnFailure(hr = E_INVALIDARG, "unknown SecureObject.Table: %ls", pwzTable);
373 }
374
375 int iCompAttributes = 0;
376 hr = WcaGetRecordInteger(hRec, QSO_COMPATTRIBUTES, &iCompAttributes);
377 ExitOnFailure(hr, "failed to get Component attributes for secure object");
378
379 BOOL fIs64Bit = iCompAttributes & msidbComponentAttributes64bit;
380
381 // Only process entries in the Wix4SecureObject table whose components match the bitness of this CA
382#ifdef _WIN64
383 if (!fIs64Bit)
384 {
385 continue;
386 }
387#else
388 if (fIs64Bit)
389 {
390 continue;
391 }
392#endif
393
394 // Get the object to secure
395 hr = WcaGetRecordString(hRec, QSO_SECUREOBJECT, &pwzSecureObject);
396 ExitOnFailure(hr, "failed to get name of object");
397
398 hr = GetTargetPath(eType, pwzSecureObject, &pwzTargetPath);
399 ExitOnFailure(hr, "failed to get target path of object '%ls'", pwzSecureObject);
400
401 hr = WcaGetRecordString(hRec, QSO_COMPONENT, &pwzData);
402 ExitOnFailure(hr, "failed to get Component name for secure object");
403
404 //
405 // if we are installing this Component
406 //
407 er = ::MsiGetComponentStateW(hInstall, pwzData, &isInstalled, &isAction);
408 ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "failed to get install state for Component: %ls", pwzData);
409
410 if (WcaIsInstalling(isInstalled, isAction))
411 {
412 hr = WcaWriteStringToCaData(pwzTargetPath, &pwzCustomActionData);
413 ExitOnFailure(hr, "failed to add data to CustomActionData");
414
415 // add the data to the CustomActionData
416 hr = WcaGetRecordString(hRec, QSO_SECUREOBJECT, &pwzData);
417 ExitOnFailure(hr, "failed to get name of object");
418 hr = WcaWriteStringToCaData(pwzTable, &pwzCustomActionData);
419 ExitOnFailure(hr, "failed to add data to CustomActionData");
420
421 hr = WcaGetRecordFormattedString(hRec, QSO_DOMAIN, &pwzData);
422 ExitOnFailure(hr, "failed to get domain for user to configure object");
423 hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData);
424 ExitOnFailure(hr, "failed to add data to CustomActionData");
425
426 hr = WcaGetRecordFormattedString(hRec, QSO_USER, &pwzData);
427 ExitOnFailure(hr, "failed to get user to configure object");
428 hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData);
429 ExitOnFailure(hr, "failed to add data to CustomActionData");
430
431 hr = WcaGetRecordInteger(hRec, QSO_ATTRIBUTES, reinterpret_cast<int*>(&dwAttributes));
432 ExitOnFailure(hr, "failed to get attributes to configure object");
433 hr = WcaWriteIntegerToCaData(dwAttributes, &pwzCustomActionData);
434 ExitOnFailure(hr, "failed to add data to CustomActionData");
435
436 hr = WcaGetRecordString(hRec, QSO_PERMISSION, &pwzData);
437 ExitOnFailure(hr, "failed to get permission to configure object");
438 hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData);
439 ExitOnFailure(hr, "failed to add data to CustomActionData");
440
441 ++cObjects;
442 }
443 }
444
445 // if we looped through all records all is well
446 if (E_NOMOREITEMS == hr)
447 hr = S_OK;
448 ExitOnFailure(hr, "failed while looping through all objects to secure");
449
450 //
451 // schedule the custom action and add to progress bar
452 //
453 if (pwzCustomActionData && *pwzCustomActionData)
454 {
455 Assert(0 < cObjects);
456
457 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"ExecSecureObjects"), pwzCustomActionData, cObjects * COST_SECUREOBJECT);
458 ExitOnFailure(hr, "failed to schedule ExecSecureObjects action");
459 }
460
461LExit:
462 ReleaseStr(pwzSecureObject);
463 ReleaseStr(pwzCustomActionData);
464 ReleaseStr(pwzData);
465 ReleaseStr(pwzTable);
466 ReleaseStr(pwzTargetPath);
467
468 if (FAILED(hr))
469 {
470 er = ERROR_INSTALL_FAILURE;
471 }
472 return WcaFinalize(er);
473}
474
475/******************************************************************
476 SchedSecureObjectsRollback - entry point for SchedSecureObjectsRollback Custom Action
477
478 called as Type 1 CustomAction (binary DLL) from Windows Installer
479 in InstallExecuteSequence before SchedSecureObjects
480******************************************************************/
481extern "C" UINT __stdcall SchedSecureObjectsRollback(
482 __in MSIHANDLE hInstall
483 )
484{
485// AssertSz(FALSE, "debug SchedSecureObjectsRollback");
486 HRESULT hr = S_OK;
487 UINT er = ERROR_SUCCESS;
488
489 LPWSTR pwzSecureObject = NULL;
490 LPWSTR pwzTable = NULL;
491 LPWSTR pwzTargetPath = NULL;
492
493 PMSIHANDLE hView = NULL;
494 PMSIHANDLE hRec = NULL;
495
496 LPWSTR pwzCustomActionData = NULL;
497
498 eOBJECTTYPE eType = OT_UNKNOWN;
499
500 //
501 // initialize
502 //
503 hr = WcaInitialize(hInstall, "SchedSecureObjectsRollback");
504 ExitOnFailure(hr, "failed to initialize");
505
506 //
507 // loop through all the objects to be secured
508 //
509 hr = WcaOpenExecuteView(wzQUERY_SECUREOBJECTS, &hView);
510 ExitOnFailure(hr, "failed to open view on Wix4SecureObject table");
511 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
512 {
513 hr = WcaGetRecordString(hRec, QSO_TABLE, &pwzTable);
514 ExitOnFailure(hr, "failed to get object table");
515
516 eType = EObjectTypeFromString(pwzTable);
517
518 if (OT_UNKNOWN == eType)
519 {
520 ExitOnFailure(hr = E_INVALIDARG, "unknown SecureObject.Table: %ls", pwzTable);
521 }
522
523 int iCompAttributes = 0;
524 hr = WcaGetRecordInteger(hRec, QSO_COMPATTRIBUTES, &iCompAttributes);
525 ExitOnFailure(hr, "failed to get Component attributes for secure object");
526
527 BOOL fIs64Bit = iCompAttributes & msidbComponentAttributes64bit;
528
529 // Only process entries in the Wix4SecureObject table whose components match the bitness of this CA
530#ifdef _WIN64
531 if (!fIs64Bit)
532 {
533 continue;
534 }
535#else
536 if (fIs64Bit)
537 {
538 continue;
539 }
540#endif
541
542 // get the object being secured that we are planning to schedule rollback for
543 hr = WcaGetRecordString(hRec, QSO_SECUREOBJECT, &pwzSecureObject);
544 ExitOnFailure(hr, "failed to get name of object");
545
546 hr = GetTargetPath(eType, pwzSecureObject, &pwzTargetPath);
547 ExitOnFailure(hr, "failed to get target path of object '%ls' in order to schedule rollback", pwzSecureObject);
548
549 hr = StoreACLRollbackInfo(pwzTargetPath, pwzTable);
550 if (FAILED(hr))
551 {
552 WcaLog(LOGMSG_STANDARD, "Failed to store ACL rollback information with error 0x%x - continuing", hr);
553 }
554 }
555
556 // if we looped through all records all is well
557 if (E_NOMOREITEMS == hr)
558 {
559 hr = S_OK;
560 }
561 ExitOnFailure(hr, "failed while looping through all objects to schedule rollback for");
562
563LExit:
564 ReleaseStr(pwzCustomActionData);
565 ReleaseStr(pwzSecureObject);
566 ReleaseStr(pwzTable);
567 ReleaseStr(pwzTargetPath);
568
569 if (FAILED(hr))
570 {
571 er = ERROR_INSTALL_FAILURE;
572 }
573 return WcaFinalize(er);
574}
575
576/******************************************************************
577 CaExecSecureObjects - entry point for SecureObjects Custom Action
578 called as Type 1025 CustomAction (deferred binary DLL)
579
580 NOTE: deferred CustomAction since it modifies the machine
581 NOTE: CustomActionData == wzObject\twzTable\twzDomain\twzUser\tdwAttributes\tdwPermissions\t...
582******************************************************************/
583extern "C" UINT __stdcall ExecSecureObjects(
584 __in MSIHANDLE hInstall
585 )
586{
587// AssertSz(FALSE, "debug ExecSecureObjects");
588 HRESULT hr = S_OK;
589 DWORD er = ERROR_SUCCESS;
590
591 LPWSTR pwz = NULL;
592 LPWSTR pwzData = NULL;
593 LPWSTR pwzObject = NULL;
594 LPWSTR pwzTable = NULL;
595 LPWSTR pwzDomain = NULL;
596 DWORD dwRevision = 0;
597 LPWSTR pwzUser = NULL;
598 DWORD dwPermissions = 0;
599 DWORD dwAttributes = 0;
600 LPWSTR pwzAccount = NULL;
601 PSID psid = NULL;
602
603 EXPLICIT_ACCESSW ea = {0};
604 SE_OBJECT_TYPE objectType = SE_UNKNOWN_OBJECT_TYPE;
605 PSECURITY_DESCRIPTOR psd = NULL;
606 SECURITY_DESCRIPTOR_CONTROL sdc = {0};
607 SECURITY_INFORMATION si = {0};
608 PACL pAclExisting = NULL; // doesn't get freed
609 PACL pAclNew = NULL;
610
611 PMSIHANDLE hActionRec = ::MsiCreateRecord(1);
612
613 //
614 // initialize
615 //
616 hr = WcaInitialize(hInstall, "ExecSecureObjects");
617 ExitOnFailure(hr, "failed to initialize");
618
619 hr = WcaGetProperty(L"CustomActionData", &pwzData);
620 ExitOnFailure(hr, "failed to get CustomActionData");
621
622 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData);
623
624 pwz = pwzData;
625
626 //
627 // loop through all the passed in data
628 //
629 while (pwz && *pwz)
630 {
631 hr = WcaReadStringFromCaData(&pwz, &pwzObject);
632 ExitOnFailure(hr, "failed to process CustomActionData");
633
634 hr = WcaReadStringFromCaData(&pwz, &pwzTable);
635 ExitOnFailure(hr, "failed to process CustomActionData");
636 hr = WcaReadStringFromCaData(&pwz, &pwzDomain);
637 ExitOnFailure(hr, "failed to process CustomActionData");
638 hr = WcaReadStringFromCaData(&pwz, &pwzUser);
639 ExitOnFailure(hr, "failed to process CustomActionData");
640 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&dwAttributes));
641 ExitOnFailure(hr, "failed to process CustomActionData");
642 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&dwPermissions));
643 ExitOnFailure(hr, "failed to process CustomActionData");
644
645 WcaLog(LOGMSG_VERBOSE, "Securing Object: %ls Type: %ls User: %ls", pwzObject, pwzTable, pwzUser);
646
647 //
648 // create the appropriate SID
649 //
650
651 // figure out the right user to put into the access block
652 if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"Everyone"))
653 {
654 hr = AclGetWellKnownSid(WinWorldSid, &psid);
655 }
656 else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"Administrators"))
657 {
658 hr = AclGetWellKnownSid(WinBuiltinAdministratorsSid, &psid);
659 }
660 else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"LocalSystem"))
661 {
662 hr = AclGetWellKnownSid(WinLocalSystemSid, &psid);
663 }
664 else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"LocalService"))
665 {
666 hr = AclGetWellKnownSid(WinLocalServiceSid, &psid);
667 }
668 else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"NetworkService"))
669 {
670 hr = AclGetWellKnownSid(WinNetworkServiceSid, &psid);
671 }
672 else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"AuthenticatedUser"))
673 {
674 hr = AclGetWellKnownSid(WinAuthenticatedUserSid, &psid);
675 }
676 else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"Guests"))
677 {
678 hr = AclGetWellKnownSid(WinBuiltinGuestsSid, &psid);
679 }
680 else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"CREATOR OWNER"))
681 {
682 hr = AclGetWellKnownSid(WinCreatorOwnerSid, &psid);
683 }
684 else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"INTERACTIVE"))
685 {
686 hr = AclGetWellKnownSid(WinInteractiveSid, &psid);
687 }
688 else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"Users"))
689 {
690 hr = AclGetWellKnownSid(WinBuiltinUsersSid, &psid);
691 }
692 else
693 {
694 hr = StrAllocFormatted(&pwzAccount, L"%s%s%s", pwzDomain, *pwzDomain ? L"\\" : L"", pwzUser);
695 ExitOnFailure(hr, "failed to build domain user name");
696
697 hr = AclGetAccountSid(NULL, pwzAccount, &psid);
698 }
699 ExitOnFailure(hr, "failed to get sid for account: %ls%ls%ls", pwzDomain, *pwzDomain ? L"\\" : L"", pwzUser);
700
701 //
702 // build up the explicit access
703 //
704 ea.grfAccessMode = SET_ACCESS;
705
706 if (dwAttributes & SECURE_OBJECT_ATTRIBUTE_INHERITABLE)
707 {
708 ea.grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
709 }
710 else
711 {
712 ea.grfInheritance = NO_INHERITANCE;
713 }
714
715#pragma prefast(push)
716#pragma prefast(disable:25029)
717 ::BuildTrusteeWithSidW(&ea.Trustee, psid);
718#pragma prefast(pop)
719
720 objectType = SEObjectTypeFromString(const_cast<LPCWSTR> (pwzTable));
721
722 // always add these permissions for services
723 // these are basic permissions that are often forgotten
724 if (0 == lstrcmpW(L"ServiceInstall", pwzTable))
725 {
726 dwPermissions |= SERVICE_QUERY_CONFIG | SERVICE_QUERY_STATUS | SERVICE_ENUMERATE_DEPENDENTS | SERVICE_INTERROGATE;
727 }
728
729 ea.grfAccessPermissions = dwPermissions;
730
731 if (SE_UNKNOWN_OBJECT_TYPE != objectType)
732 {
733 er = ::GetNamedSecurityInfoW(pwzObject, objectType, DACL_SECURITY_INFORMATION, NULL, NULL, &pAclExisting, NULL, &psd);
734 ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "failed to get security info for object: %ls", pwzObject);
735
736 //Need to see if DACL is protected so getting Descriptor information
737 if (!::GetSecurityDescriptorControl(psd, &sdc, &dwRevision))
738 {
739 ExitOnLastError(hr, "failed to get security descriptor control for object: %ls", pwzObject);
740 }
741
742#pragma prefast(push)
743#pragma prefast(disable:25029)
744 er = ::SetEntriesInAclW(1, &ea, pAclExisting, &pAclNew);
745#pragma prefast(pop)
746 ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "failed to add ACLs for object: %ls", pwzObject);
747
748 if (sdc & SE_DACL_PROTECTED)
749 {
750 si = DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION;
751 }
752 else
753 {
754 si = DACL_SECURITY_INFORMATION;
755 }
756 er = ::SetNamedSecurityInfoW(pwzObject, objectType, si, NULL, NULL, pAclNew, NULL);
757 MessageExitOnFailure(hr = HRESULT_FROM_WIN32(er), msierrSecureObjectsFailedSet, "failed to set security info for object: %ls", pwzObject);
758 }
759 else
760 {
761 MessageExitOnFailure(hr = E_UNEXPECTED, msierrSecureObjectsUnknownType, "unknown object type: %ls", pwzTable);
762 }
763
764 hr = WcaProgressMessage(COST_SECUREOBJECT, FALSE);
765 ExitOnFailure(hr, "failed to send progress message");
766
767 objectType = SE_UNKNOWN_OBJECT_TYPE;
768 }
769
770LExit:
771 ReleaseStr(pwzUser);
772 ReleaseStr(pwzDomain);
773 ReleaseStr(pwzTable);
774 ReleaseStr(pwzObject);
775 ReleaseStr(pwzData);
776 ReleaseStr(pwzAccount);
777
778 if (pAclNew)
779 {
780 ::LocalFree(pAclNew);
781 }
782 if (psd)
783 {
784 ::LocalFree(psd);
785 }
786 if (psid)
787 {
788 AclFreeSid(psid);
789 }
790
791 if (FAILED(hr))
792 {
793 er = ERROR_INSTALL_FAILURE;
794 }
795 return WcaFinalize(er);
796}
797
798extern "C" UINT __stdcall ExecSecureObjectsRollback(
799 __in MSIHANDLE hInstall
800 )
801{
802// AssertSz(FALSE, "debug ExecSecureObjectsRollback");
803 HRESULT hr = S_OK;
804 DWORD er = ERROR_SUCCESS;
805
806 LPWSTR pwz = NULL;
807 LPWSTR pwzData = NULL;
808 LPWSTR pwzObject = NULL;
809 LPWSTR pwzTable = NULL;
810 LPWSTR pwzSecurityInfo = NULL;
811
812 SE_OBJECT_TYPE objectType = SE_UNKNOWN_OBJECT_TYPE;
813 PSECURITY_DESCRIPTOR psd = NULL;
814 ULONG psdSize;
815 SECURITY_DESCRIPTOR_CONTROL sdc = {0};
816 SECURITY_INFORMATION si = DACL_SECURITY_INFORMATION;
817 PACL pDacl = NULL;
818 BOOL bDaclPresent = false;
819 BOOL bDaclDefaulted = false;
820 DWORD dwRevision = 0;
821 int iProtected;
822
823 // initialize
824 hr = WcaInitialize(hInstall, "ExecSecureObjectsRollback");
825 ExitOnFailure(hr, "failed to initialize");
826
827 hr = WcaGetProperty(L"CustomActionData", &pwzData);
828 ExitOnFailure(hr, "failed to get CustomActionData");
829
830 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData);
831
832 pwz = pwzData;
833
834 hr = WcaReadStringFromCaData(&pwz, &pwzObject);
835 ExitOnFailure(hr, "failed to process CustomActionData");
836
837 hr = WcaReadStringFromCaData(&pwz, &pwzTable);
838 ExitOnFailure(hr, "failed to process CustomActionData");
839
840 objectType = SEObjectTypeFromString(const_cast<LPCWSTR> (pwzTable));
841
842 if (SE_UNKNOWN_OBJECT_TYPE != objectType)
843 {
844 hr = WcaReadStringFromCaData(&pwz, &pwzSecurityInfo);
845 ExitOnFailure(hr, "failed to process CustomActionData");
846
847 hr = WcaReadIntegerFromCaData(&pwz, &iProtected);
848 ExitOnFailure(hr, "failed to process CustomActionData");
849
850 if (!::ConvertStringSecurityDescriptorToSecurityDescriptorW(pwzSecurityInfo,SDDL_REVISION_1,&psd,&psdSize))
851 {
852 ExitOnLastError(hr, "failed to convert security descriptor string to a valid security descriptor");
853 }
854
855 if (!::GetSecurityDescriptorDacl(psd,&bDaclPresent,&pDacl,&bDaclDefaulted))
856 {
857 hr = E_UNEXPECTED;
858 ExitOnFailure(hr, "failed to get security descriptor's DACL - error code: %d",pwzSecurityInfo,GetLastError());
859 }
860
861 // The below situation may always be caught by the above if block - the documentation isn't very clear. To be safe, we're going to test for it.
862 if (!bDaclPresent)
863 {
864 hr = E_UNEXPECTED;
865 ExitOnFailure(hr, "security descriptor does not contain a DACL");
866 }
867
868 //Need to see if DACL is protected so getting Descriptor information
869 if (!::GetSecurityDescriptorControl(psd, &sdc, &dwRevision))
870 {
871 ExitOnLastError(hr, "failed to get security descriptor control for object: %ls", pwzObject);
872 }
873
874 // Write a 1 if DACL is protected, 0 otherwise
875 switch (iProtected)
876 {
877 case 0:
878 // Unnecessary to do anything - leave si to the default flags
879 break;
880
881 case 1:
882 si = si | PROTECTED_DACL_SECURITY_INFORMATION;
883 break;
884
885 default:
886 hr = E_UNEXPECTED;
887 ExitOnFailure(hr, "unrecognized value in CustomActionData");
888 break;
889 }
890
891 er = ::SetNamedSecurityInfoW(pwzObject, objectType, si, NULL, NULL, pDacl, NULL);
892 ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "failed to set security info for object: %ls error code: %d", pwzObject, GetLastError());
893 }
894 else
895 {
896 MessageExitOnFailure(hr = E_UNEXPECTED, msierrSecureObjectsUnknownType, "unknown object type: %ls", pwzTable);
897 }
898
899LExit:
900 ReleaseStr(pwzData);
901 ReleaseStr(pwzObject);
902 ReleaseStr(pwzTable);
903 ReleaseStr(pwzSecurityInfo);
904
905 if (psd)
906 {
907 ::LocalFree(psd);
908 }
909
910 if (FAILED(hr))
911 {
912 er = ERROR_INSTALL_FAILURE;
913 }
914 return WcaFinalize(er);
915}
diff --git a/src/ext/Util/ca/serviceconfig.cpp b/src/ext/Util/ca/serviceconfig.cpp
new file mode 100644
index 00000000..04b25ffa
--- /dev/null
+++ b/src/ext/Util/ca/serviceconfig.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// structs
6LPCWSTR wzQUERY_SERVICECONFIG = L"SELECT `ServiceName`, `Component_`, `NewService`, `FirstFailureActionType`, `SecondFailureActionType`, `ThirdFailureActionType`, `ResetPeriodInDays`, `RestartServiceDelayInSeconds`, `ProgramCommandLine`, `RebootMessage` FROM `Wix4ServiceConfig`";
7enum eQUERY_SERVICECONFIG { QSC_SERVICENAME = 1, QSC_COMPONENT, QSC_NEWSERVICE, QSC_FIRSTFAILUREACTIONTYPE, QSC_SECONDFAILUREACTIONTYPE, QSC_THIRDFAILUREACTIONTYPE, QSC_RESETPERIODINDAYS, QSC_RESTARTSERVICEDELAYINSECONDS, QSC_PROGRAMCOMMANDLINE, QSC_REBOOTMESSAGE };
8
9// consts
10LPCWSTR c_wzActionTypeNone = L"none";
11LPCWSTR c_wzActionTypeReboot = L"reboot";
12LPCWSTR c_wzActionTypeRestart = L"restart";
13LPCWSTR c_wzActionTypeRunCommand = L"runCommand";
14
15// prototypes
16static SC_ACTION_TYPE GetSCActionType(
17 __in LPCWSTR pwzActionTypeName
18 );
19
20static HRESULT GetSCActionTypeString(
21 __in SC_ACTION_TYPE type,
22 __out_ecount(cchActionTypeString) LPWSTR wzActionTypeString,
23 __in DWORD cchActionTypeString
24 );
25
26static HRESULT GetService(
27 __in SC_HANDLE hSCM,
28 __in LPCWSTR wzService,
29 __in DWORD dwOpenServiceAccess,
30 __out SC_HANDLE* phService
31 );
32
33static HRESULT ConfigureService(
34 __in SC_HANDLE hSCM,
35 __in SC_HANDLE hService,
36 __in LPCWSTR wzServiceName,
37 __in DWORD dwRestartServiceDelayInSeconds,
38 __in LPCWSTR wzFirstFailureActionType,
39 __in LPCWSTR wzSecondFailureActionType,
40 __in LPCWSTR wzThirdFailureActionType,
41 __in DWORD dwResetPeriodInDays,
42 __in LPWSTR wzRebootMessage,
43 __in LPWSTR wzProgramCommandLine
44 );
45
46
47/******************************************************************
48SchedServiceConfig - entry point for SchedServiceConfig Custom Action
49
50called as Type 1 CustomAction (binary DLL) from Windows Installer
51in InstallExecuteSequence before CaExecServiceConfig
52********************************************************************/
53extern "C" UINT __stdcall SchedServiceConfig(
54 __in MSIHANDLE hInstall
55 )
56{
57 //AssertSz(FALSE, "debug SchedServiceConfig");
58 HRESULT hr = S_OK;
59 UINT er = ERROR_SUCCESS;
60
61 LPWSTR pwzScriptKey = NULL;
62 LPWSTR pwzCustomActionData = NULL;
63
64 PMSIHANDLE hView = NULL;
65 PMSIHANDLE hRec = NULL;
66 LPWSTR pwzData = NULL;
67 int iData = 0;
68 DWORD cServices = 0;
69
70 // initialize
71 hr = WcaInitialize(hInstall, "SchedServiceConfig");
72 ExitOnFailure(hr, "Failed to initialize.");
73
74 // Get the script key for this CustomAction and put it on the front of the
75 // CustomActionData of the install action.
76 hr = WcaCaScriptCreateKey(&pwzScriptKey);
77 ExitOnFailure(hr, "Failed to get encoding key.");
78
79 hr = WcaWriteStringToCaData(pwzScriptKey, &pwzCustomActionData);
80 ExitOnFailure(hr, "Failed to add encoding key to CustomActionData.");
81
82 // Loop through all the services to be configured.
83 hr = WcaOpenExecuteView(wzQUERY_SERVICECONFIG, &hView);
84 ExitOnFailure(hr, "Failed to open view on Wix4ServiceConfig table.");
85
86 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
87 {
88 INSTALLSTATE isInstalled = INSTALLSTATE_UNKNOWN;
89 INSTALLSTATE isAction = INSTALLSTATE_UNKNOWN;
90
91 // Get component name to check if we are installing it. If so
92 // then add the table data to the CustomActionData, otherwise
93 // skip it.
94 hr = WcaGetRecordString(hRec, QSC_COMPONENT, &pwzData);
95 ExitOnFailure(hr, "Failed to get component name");
96
97 hr = ::MsiGetComponentStateW(hInstall, pwzData, &isInstalled, &isAction);
98 ExitOnFailure(hr = HRESULT_FROM_WIN32(hr), "Failed to get install state for Component: %ls", pwzData);
99
100 if (WcaIsInstalling(isInstalled, isAction))
101 {
102 // Add the data to the CustomActionData (for install).
103 hr = WcaGetRecordFormattedString(hRec, QSC_SERVICENAME, &pwzData);
104 ExitOnFailure(hr, "Failed to get name of service.");
105 hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData);
106 ExitOnFailure(hr, "Failed to add name to CustomActionData.");
107
108 hr = WcaGetRecordInteger(hRec, QSC_NEWSERVICE, &iData);
109 ExitOnFailure(hr, "Failed to get Wix4ServiceConfig.NewService.");
110 hr = WcaWriteIntegerToCaData(0 != iData, &pwzCustomActionData);
111 ExitOnFailure(hr, "Failed to add NewService data to CustomActionData");
112
113 hr = WcaGetRecordString(hRec, QSC_FIRSTFAILUREACTIONTYPE, &pwzData);
114 ExitOnFailure(hr, "failed to get first failure action type");
115 hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData);
116 ExitOnFailure(hr, "failed to add data to CustomActionData");
117
118 hr = WcaGetRecordString(hRec, QSC_SECONDFAILUREACTIONTYPE, &pwzData);
119 ExitOnFailure(hr, "failed to get second failure action type");
120 hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData);
121 ExitOnFailure(hr, "failed to add data to CustomActionData");
122
123 hr = WcaGetRecordString(hRec, QSC_THIRDFAILUREACTIONTYPE, &pwzData);
124 ExitOnFailure(hr, "failed to get third failure action type");
125 hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData);
126 ExitOnFailure(hr, "failed to add data to CustomActionData");
127
128 hr = WcaGetRecordInteger(hRec, QSC_RESETPERIODINDAYS, &iData);
129 if (S_FALSE == hr) // deal w/ possible null value
130 {
131 iData = 0;
132 }
133 ExitOnFailure(hr, "failed to get reset period in days between service restart attempts.");
134 hr = WcaWriteIntegerToCaData(iData, &pwzCustomActionData);
135 ExitOnFailure(hr, "failed to add data to CustomActionData");
136
137 hr = WcaGetRecordInteger(hRec, QSC_RESTARTSERVICEDELAYINSECONDS, &iData);
138 if (S_FALSE == hr) // deal w/ possible null value
139 {
140 iData = 0;
141 }
142 ExitOnFailure(hr, "failed to get server restart delay value.");
143 hr = WcaWriteIntegerToCaData(iData, &pwzCustomActionData);
144 ExitOnFailure(hr, "failed to add data to CustomActionData");
145
146 hr = WcaGetRecordFormattedString(hRec, QSC_PROGRAMCOMMANDLINE, &pwzData); // null value already dealt w/ properly
147 ExitOnFailure(hr, "failed to get command line to run on service failure.");
148 hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData);
149 ExitOnFailure(hr, "failed to add data to CustomActionData");
150
151 hr = WcaGetRecordString(hRec, QSC_REBOOTMESSAGE, &pwzData); // null value already dealt w/ properly
152 ExitOnFailure(hr, "failed to get message to send to users when server reboots due to service failure.");
153 hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData);
154 ExitOnFailure(hr, "failed to add data to CustomActionData");
155
156 ++cServices;
157 }
158 }
159
160 // if we looped through all records all is well
161 if (E_NOMOREITEMS == hr)
162 {
163 hr = S_OK;
164 }
165 ExitOnFailure(hr, "failed while looping through all objects to secure");
166
167 // setup CustomActionData and add to progress bar for download
168 if (0 < cServices)
169 {
170 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"RollbackServiceConfig"), pwzScriptKey, cServices * COST_SERVICECONFIG);
171 ExitOnFailure(hr, "failed to schedule RollbackServiceConfig action");
172
173 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"ExecServiceConfig"), pwzCustomActionData, cServices * COST_SERVICECONFIG);
174 ExitOnFailure(hr, "failed to schedule ExecServiceConfig action");
175 }
176
177LExit:
178 ReleaseStr(pwzData);
179 ReleaseStr(pwzCustomActionData);
180 ReleaseStr(pwzScriptKey);
181
182 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
183 return WcaFinalize(er);
184}
185
186
187/******************************************************************
188CaExecServiceConfig - entry point for ServiceConfig Custom Action.
189
190NOTE: deferred CustomAction since it modifies the machine
191NOTE: CustomActionData == wzServiceName\tfNewService\twzFirstFailureActionType\twzSecondFailureActionType\twzThirdFailureActionType\tdwResetPeriodInDays\tdwRestartServiceDelayInSeconds\twzProgramCommandLine\twzRebootMessage\twzServiceName\tfNewService\t...
192*******************************************************************/
193extern "C" UINT __stdcall ExecServiceConfig(
194 __in MSIHANDLE hInstall
195 )
196{
197 //AssertSz(FALSE, "debug ExecServiceConfig");
198 HRESULT hr = S_OK;
199 DWORD er = 0;
200
201 LPWSTR pwzCustomActionData = NULL;
202 LPWSTR pwz = NULL;
203
204 LPWSTR pwzScriptKey = NULL;
205 WCA_CASCRIPT_HANDLE hRollbackScript = NULL;
206
207 LPWSTR pwzServiceName = NULL;
208 BOOL fNewService = FALSE;
209 LPWSTR pwzFirstFailureActionType = NULL;
210 LPWSTR pwzSecondFailureActionType = NULL;
211 LPWSTR pwzThirdFailureActionType = NULL;
212 LPWSTR pwzProgramCommandLine = NULL;
213 LPWSTR pwzRebootMessage = NULL;
214 DWORD dwResetPeriodInDays = 0;
215 DWORD dwRestartServiceDelayInSeconds = 0;
216
217 LPVOID lpMsgBuf = NULL;
218 SC_HANDLE hSCM = NULL;
219 SC_HANDLE hService = NULL;
220
221 DWORD dwRestartDelay = 0;
222 WCHAR wzActionName[32] = { 0 };
223
224 DWORD cbExistingServiceConfig = 0;
225
226 SERVICE_FAILURE_ACTIONSW* psfa = NULL;
227
228 // initialize
229 hr = WcaInitialize(hInstall, "ExecServiceConfig");
230 ExitOnFailure(hr, "failed to initialize");
231
232 // Open the Services Control Manager up front.
233 hSCM = ::OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
234 if (NULL == hSCM)
235 {
236 er = ::GetLastError();
237 hr = HRESULT_FROM_WIN32(er);
238
239#pragma prefast(push)
240#pragma prefast(disable:25028)
241 ::FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, er, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&lpMsgBuf, 0, NULL);
242#pragma prefast(pop)
243
244 ExitOnFailure(hr, "Failed to get handle to SCM. Error: %ls", (LPWSTR)lpMsgBuf);
245 }
246
247 // First, get the script key out of the CustomActionData and
248 // use that to create the rollback script for this action.
249 hr = WcaGetProperty( L"CustomActionData", &pwzCustomActionData);
250 ExitOnFailure(hr, "failed to get CustomActionData");
251
252 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzCustomActionData);
253
254 pwz = pwzCustomActionData;
255
256 hr = WcaReadStringFromCaData(&pwz, &pwzScriptKey);
257 if (!pwzScriptKey)
258 {
259 hr = E_UNEXPECTED;
260 ExitOnFailure(hr, "Failed due to unexpected CustomActionData passed.");
261 }
262 ExitOnFailure(hr, "Failed to read encoding key from CustomActionData.");
263
264 hr = WcaCaScriptCreate(WCA_ACTION_INSTALL, WCA_CASCRIPT_ROLLBACK, FALSE, pwzScriptKey, FALSE, &hRollbackScript);
265 ExitOnFailure(hr, "Failed to open rollback CustomAction script.");
266
267 // Next, loop through the rest of the CustomActionData, processing
268 // each service config row in turn.
269 while (pwz && *pwz)
270 {
271 hr = WcaReadStringFromCaData(&pwz, &pwzServiceName);
272 ExitOnFailure(hr, "failed to process CustomActionData");
273 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&fNewService));
274 ExitOnFailure(hr, "failed to process CustomActionData");
275 hr = WcaReadStringFromCaData(&pwz, &pwzFirstFailureActionType);
276 ExitOnFailure(hr, "failed to process CustomActionData");
277 hr = WcaReadStringFromCaData(&pwz, &pwzSecondFailureActionType);
278 ExitOnFailure(hr, "failed to process CustomActionData");
279 hr = WcaReadStringFromCaData(&pwz, &pwzThirdFailureActionType);
280 ExitOnFailure(hr, "failed to process CustomActionData");
281 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&dwResetPeriodInDays));
282 ExitOnFailure(hr, "failed to process CustomActionData");
283 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&dwRestartServiceDelayInSeconds));
284 ExitOnFailure(hr, "failed to process CustomActionData");
285 hr = WcaReadStringFromCaData(&pwz, &pwzProgramCommandLine);
286 ExitOnFailure(hr, "failed to process CustomActionData");
287 hr = WcaReadStringFromCaData(&pwz, &pwzRebootMessage);
288 ExitOnFailure(hr, "failed to process CustomActionData");
289
290 WcaLog(LOGMSG_VERBOSE, "Configuring Service: %ls", pwzServiceName);
291
292 // Open the handle with all the permissions we might need:
293 // SERVICE_QUERY_CONFIG is needed for QueryServiceConfig2().
294 // SERVICE_CHANGE_CONFIG is needed for ChangeServiceConfig2().
295 // SERVICE_START is required in order to handle SC_ACTION_RESTART action.
296 hr = GetService(hSCM, pwzServiceName, SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG | SERVICE_START, &hService);
297 ExitOnFailure(hr, "Failed to get service: %ls", pwzServiceName);
298
299 // If we are configuring a service that existed on the machine, we need to
300 // read the existing service configuration and write it out to the rollback
301 // log so rollback can put it back if anything goes wrong.
302 if (!fNewService)
303 {
304 // First, read the existing service config.
305 if (!::QueryServiceConfig2W(hService, SERVICE_CONFIG_FAILURE_ACTIONS, NULL, 0, &cbExistingServiceConfig) && ERROR_INSUFFICIENT_BUFFER != ::GetLastError())
306 {
307 ExitWithLastError(hr, "Failed to get current service config info.");
308 }
309
310 psfa = static_cast<LPSERVICE_FAILURE_ACTIONSW>(MemAlloc(cbExistingServiceConfig, TRUE));
311 ExitOnNull(psfa, hr, E_OUTOFMEMORY, "failed to allocate memory for service failure actions.");
312
313 if (!::QueryServiceConfig2W(hService, SERVICE_CONFIG_FAILURE_ACTIONS, (LPBYTE)psfa, cbExistingServiceConfig, &cbExistingServiceConfig))
314 {
315 ExitOnLastError(hr, "failed to Query Service.");
316 }
317
318 // Build up rollback log so we can restore service state if necessary
319 hr = WcaCaScriptWriteString(hRollbackScript, pwzServiceName);
320 ExitOnFailure(hr, "Failed to add service name to Rollback Log");
321
322 // If this service struct is empty, fill in default values
323 if (3 > psfa->cActions)
324 {
325 hr = WcaCaScriptWriteString(hRollbackScript, c_wzActionTypeNone);
326 ExitOnFailure(hr, "failed to add data to Rollback CustomActionData");
327
328 hr = WcaCaScriptWriteString(hRollbackScript, c_wzActionTypeNone);
329 ExitOnFailure(hr, "failed to add data to Rollback CustomActionData");
330
331 hr = WcaCaScriptWriteString(hRollbackScript, c_wzActionTypeNone);
332 ExitOnFailure(hr, "failed to add data to Rollback CustomActionData");
333 }
334 else
335 {
336 // psfa actually had actions defined, so use the first three.
337 for (int i = 0; i < 3; ++i)
338 {
339 hr = GetSCActionTypeString(psfa->lpsaActions[i].Type, wzActionName, countof(wzActionName));
340 ExitOnFailure(hr, "failed to query SFA object");
341
342 if (SC_ACTION_RESTART == psfa->lpsaActions[i].Type)
343 {
344 dwRestartDelay = psfa->lpsaActions[i].Delay / 1000;
345 }
346
347 hr = WcaCaScriptWriteString(hRollbackScript, wzActionName);
348 ExitOnFailure(hr, "failed to add data to Rollback CustomActionData");
349 }
350 }
351
352 hr = WcaCaScriptWriteNumber(hRollbackScript, psfa->dwResetPeriod / (24 * 60 * 60));
353 ExitOnFailure(hr, "failed to add data to CustomActionData");
354
355 hr = WcaCaScriptWriteNumber(hRollbackScript, dwRestartDelay);
356 ExitOnFailure(hr, "failed to add data to CustomActionData");
357
358 // Handle the null cases.
359 if (!psfa->lpCommand)
360 {
361 psfa->lpCommand = L"";
362 }
363 hr = WcaCaScriptWriteString(hRollbackScript, psfa->lpCommand);
364 ExitOnFailure(hr, "failed to add data to Rollback CustomActionData");
365
366 // Handle the null cases.
367 if (!psfa->lpRebootMsg)
368 {
369 psfa->lpRebootMsg = L"";
370 }
371 hr = WcaCaScriptWriteString(hRollbackScript, psfa->lpRebootMsg);
372 ExitOnFailure(hr, "failed to add data to Rollback CustomActionData");
373
374 // Nudge the system to get all our rollback data written to disk.
375 WcaCaScriptFlush(hRollbackScript);
376
377 ReleaseNullMem(psfa);
378 }
379
380 hr = ConfigureService(hSCM, hService, pwzServiceName, dwRestartServiceDelayInSeconds, pwzFirstFailureActionType,
381 pwzSecondFailureActionType, pwzThirdFailureActionType, dwResetPeriodInDays, pwzRebootMessage, pwzProgramCommandLine);
382 ExitOnFailure(hr, "Failed to configure service: %ls", pwzServiceName);
383
384 hr = WcaProgressMessage(COST_SERVICECONFIG, FALSE);
385 ExitOnFailure(hr, "failed to send progress message");
386
387 // Per-service cleanup
388 ::CloseServiceHandle(hService);
389 hService = NULL;
390 dwResetPeriodInDays = 0;
391 dwRestartServiceDelayInSeconds = 0;
392 }
393
394LExit:
395 WcaCaScriptClose(hRollbackScript, WCA_CASCRIPT_CLOSE_PRESERVE);
396
397 if (lpMsgBuf)
398 {
399 ::LocalFree(lpMsgBuf);
400 }
401
402 if (hService)
403 {
404 ::CloseServiceHandle(hService);
405 }
406
407 if (hSCM)
408 {
409 ::CloseServiceHandle(hSCM);
410 }
411
412 ReleaseMem(psfa);
413
414 ReleaseStr(pwzRebootMessage);
415 ReleaseStr(pwzProgramCommandLine);
416 ReleaseStr(pwzThirdFailureActionType);
417 ReleaseStr(pwzSecondFailureActionType);
418 ReleaseStr(pwzFirstFailureActionType);
419 ReleaseStr(pwzServiceName);
420 ReleaseStr(pwzScriptKey);
421 ReleaseStr(pwzCustomActionData);
422
423 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
424 return WcaFinalize(er);
425}
426
427
428/******************************************************************
429RollbackServiceConfig - entry point for ServiceConfig rollback
430 Custom Action.
431
432NOTE: CustomActionScript Data == wzServiceName\twzFirstFailureActionType\twzSecondFailureActionType\twzThirdFailureActionType\tdwResetPeriodInDays\tdwRestartServiceDelayInSeconds\twzProgramCommandLine\twzRebootMessage\twzServiceName\t...
433*******************************************************************/
434extern "C" UINT __stdcall RollbackServiceConfig(
435 __in MSIHANDLE hInstall
436 )
437{
438 //AssertSz(FALSE, "debug RollbackServiceConfig");
439 HRESULT hr = S_OK;
440 DWORD er = 0;
441
442 LPWSTR pwzCustomActionData = NULL;
443 LPWSTR pwz = NULL;
444
445 LPWSTR pwzScriptKey = NULL;
446 WCA_CASCRIPT_HANDLE hRollbackScript = NULL;
447
448 LPWSTR pwzServiceName = NULL;
449 LPWSTR pwzFirstFailureActionType = NULL;
450 LPWSTR pwzSecondFailureActionType = NULL;
451 LPWSTR pwzThirdFailureActionType = NULL;
452 LPWSTR pwzProgramCommandLine = NULL;
453 LPWSTR pwzRebootMessage = NULL;
454 DWORD dwResetPeriodInDays = 0;
455 DWORD dwRestartServiceDelayInSeconds = 0;
456
457 LPVOID lpMsgBuf = NULL;
458 SC_HANDLE hSCM = NULL;
459 SC_HANDLE hService = NULL;
460
461 // initialize
462 hr = WcaInitialize(hInstall, "RollbackServiceConfig");
463 ExitOnFailure(hr, "Failed to initialize 'RollbackServiceConfig'.");
464
465 // Open the Services Control Manager up front.
466 hSCM = ::OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
467 if (NULL == hSCM)
468 {
469 er = ::GetLastError();
470 hr = HRESULT_FROM_WIN32(er);
471
472#pragma prefast(push)
473#pragma prefast(disable:25028)
474 ::FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, er, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&lpMsgBuf, 0, NULL);
475#pragma prefast(pop)
476
477 ExitOnFailure(hr, "Failed to get handle to SCM. Error: %ls", (LPWSTR)lpMsgBuf);
478
479 // Make sure we still abort, in case hSCM was NULL but no error was returned from GetLastError
480 ExitOnNull(hSCM, hr, E_POINTER, "Getting handle to SCM reported success, but no handle was returned.");
481 }
482
483 // Get the script key from the CustomAction data and use it to open
484 // the rollback log and read the data over the CustomActionData
485 // because all of the information is in the script data not the
486 // CustomActionData.
487 hr = WcaGetProperty( L"CustomActionData", &pwzCustomActionData);
488 ExitOnFailure(hr, "failed to get CustomActionData");
489
490 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzCustomActionData);
491
492 pwz = pwzCustomActionData;
493
494 hr = WcaReadStringFromCaData(&pwz, &pwzScriptKey);
495 if (!pwzScriptKey)
496 {
497 hr = E_UNEXPECTED;
498 ExitOnFailure(hr, "Failed due to unexpected CustomActionData passed.");
499 }
500 ExitOnFailure(hr, "Failed to read encoding key from CustomActionData.");
501
502 hr = WcaCaScriptOpen(WCA_ACTION_INSTALL, WCA_CASCRIPT_ROLLBACK, FALSE, pwzScriptKey, &hRollbackScript);
503 ExitOnFailure(hr, "Failed to open rollback CustomAction script.");
504
505 hr = WcaCaScriptReadAsCustomActionData(hRollbackScript, &pwzCustomActionData);
506 ExitOnFailure(hr, "Failed to read rollback script into CustomAction data.");
507
508 // Loop through the script's CustomActionData, processing each
509 // service config in turn.
510 pwz = pwzCustomActionData;
511 while (pwz && *pwz)
512 {
513 hr = WcaReadStringFromCaData(&pwz, &pwzServiceName);
514 ExitOnFailure(hr, "failed to process CustomActionData");
515 hr = WcaReadStringFromCaData(&pwz, &pwzFirstFailureActionType);
516 ExitOnFailure(hr, "failed to process CustomActionData");
517 hr = WcaReadStringFromCaData(&pwz, &pwzSecondFailureActionType);
518 ExitOnFailure(hr, "failed to process CustomActionData");
519 hr = WcaReadStringFromCaData(&pwz, &pwzThirdFailureActionType);
520 ExitOnFailure(hr, "failed to process CustomActionData");
521 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&dwResetPeriodInDays));
522 ExitOnFailure(hr, "failed to process CustomActionData");
523 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&dwRestartServiceDelayInSeconds));
524 ExitOnFailure(hr, "failed to process CustomActionData");
525 hr = WcaReadStringFromCaData(&pwz, &pwzProgramCommandLine);
526 ExitOnFailure(hr, "failed to process CustomActionData");
527 hr = WcaReadStringFromCaData(&pwz, &pwzRebootMessage);
528 ExitOnFailure(hr, "failed to process CustomActionData");
529
530 WcaLog(LOGMSG_VERBOSE, "Reconfiguring Service: %ls", pwzServiceName);
531
532 // Open the handle with all the permissions we might need.
533 // SERVICE_CHANGE_CONFIG is needed for ChangeServiceConfig2().
534 // SERVICE_START is required in order to handle SC_ACTION_RESTART action.
535 hr = GetService(hSCM, pwzServiceName, SERVICE_CHANGE_CONFIG | SERVICE_START, &hService);
536 ExitOnFailure(hr, "Failed to get service: %ls", pwzServiceName);
537
538 hr = ConfigureService(hSCM, hService, pwzServiceName, dwRestartServiceDelayInSeconds, pwzFirstFailureActionType,
539 pwzSecondFailureActionType, pwzThirdFailureActionType, dwResetPeriodInDays, pwzRebootMessage, pwzProgramCommandLine);
540 ExitOnFailure(hr, "Failed to configure service: %ls", pwzServiceName);
541
542 hr = WcaProgressMessage(COST_SERVICECONFIG, FALSE);
543 ExitOnFailure(hr, "failed to send progress message");
544
545 // Per-service cleanup
546 ::CloseServiceHandle(hService);
547 hService = NULL;
548 dwResetPeriodInDays = 0;
549 dwRestartServiceDelayInSeconds = 0;
550 }
551
552LExit:
553 if (lpMsgBuf) // Allocated with FormatString.
554 {
555 ::LocalFree(lpMsgBuf);
556 }
557
558 if (hService)
559 {
560 ::CloseServiceHandle(hService);
561 }
562
563 if (hSCM)
564 {
565 ::CloseServiceHandle(hSCM);
566 }
567
568 WcaCaScriptClose(hRollbackScript, WCA_CASCRIPT_CLOSE_DELETE);
569
570 ReleaseStr(pwzRebootMessage);
571 ReleaseStr(pwzProgramCommandLine);
572 ReleaseStr(pwzThirdFailureActionType);
573 ReleaseStr(pwzSecondFailureActionType);
574 ReleaseStr(pwzFirstFailureActionType);
575 ReleaseStr(pwzServiceName);
576 ReleaseStr(pwzScriptKey);
577 ReleaseStr(pwzCustomActionData);
578
579 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
580 return WcaFinalize(er);
581}
582
583
584/**********************************************************
585GetSCActionType - helper function to return the SC_ACTION_TYPE
586for a given string matching the allowed set.
587REBOOT, RESTART, RUN_COMMAND and NONE
588**********************************************************/
589static SC_ACTION_TYPE GetSCActionType(
590 __in LPCWSTR pwzActionTypeName
591 )
592{
593 SC_ACTION_TYPE actionType;
594
595 // verify that action types are valid. if not, just default to NONE
596 if (0 == lstrcmpiW(c_wzActionTypeReboot, pwzActionTypeName))
597 {
598 actionType = SC_ACTION_REBOOT;
599 }
600 else if (0 == lstrcmpiW(c_wzActionTypeRestart, pwzActionTypeName))
601 {
602 actionType = SC_ACTION_RESTART;
603 }
604 else if (0 == lstrcmpiW(c_wzActionTypeRunCommand, pwzActionTypeName))
605 {
606 actionType = SC_ACTION_RUN_COMMAND;
607 }
608 else
609 {
610 // default to none
611 actionType = SC_ACTION_NONE;
612 }
613
614 return actionType;
615}
616
617
618static HRESULT GetSCActionTypeString(
619 __in SC_ACTION_TYPE type,
620 __out_ecount(cchActionTypeString) LPWSTR wzActionTypeString,
621 __in DWORD cchActionTypeString
622 )
623{
624 HRESULT hr = S_OK;
625
626 switch (type)
627 {
628 case SC_ACTION_REBOOT:
629 hr = StringCchCopyW(wzActionTypeString, cchActionTypeString, c_wzActionTypeReboot);
630 ExitOnFailure(hr, "Failed to copy 'reboot' into action type.");
631 break;
632 case SC_ACTION_RESTART:
633 hr = StringCchCopyW(wzActionTypeString, cchActionTypeString, c_wzActionTypeRestart);
634 ExitOnFailure(hr, "Failed to copy 'restart' into action type.");
635 break;
636 case SC_ACTION_RUN_COMMAND:
637 hr = StringCchCopyW(wzActionTypeString, cchActionTypeString, c_wzActionTypeRunCommand);
638 ExitOnFailure(hr, "Failed to copy 'runCommand' into action type.");
639 break;
640 case SC_ACTION_NONE:
641 hr = StringCchCopyW(wzActionTypeString, cchActionTypeString, c_wzActionTypeNone);
642 ExitOnFailure(hr, "Failed to copy 'none' into action type.");
643 break;
644 default:
645 break;
646 }
647
648LExit:
649 return hr;
650}
651
652
653static HRESULT GetService(
654 __in SC_HANDLE hSCM,
655 __in LPCWSTR wzService,
656 __in DWORD dwOpenServiceAccess,
657 __out SC_HANDLE* phService
658 )
659{
660 HRESULT hr = S_OK;
661 DWORD er = ERROR_SUCCESS;
662 LPVOID lpMsgBuf = NULL;
663
664 *phService = ::OpenServiceW(hSCM, wzService, dwOpenServiceAccess);
665 if (NULL == *phService)
666 {
667 er = ::GetLastError();
668 hr = HRESULT_FROM_WIN32(er);
669 if (ERROR_SERVICE_DOES_NOT_EXIST == er)
670 {
671 ExitOnFailure(hr, "Service '%ls' does not exist on this system.", wzService);
672 }
673 else
674 {
675#pragma prefast(push)
676#pragma prefast(disable:25028)
677 ::FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, er, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&lpMsgBuf, 0, NULL);
678#pragma prefast(pop)
679
680 ExitOnFailure(hr, "Failed to get handle to the service '%ls'. Error: %ls", wzService, (LPWSTR)lpMsgBuf);
681 }
682 }
683
684LExit:
685 if (lpMsgBuf) // Allocated with FormatString.
686 {
687 ::LocalFree(lpMsgBuf);
688 }
689
690 return hr;
691}
692
693
694static HRESULT ConfigureService(
695 __in SC_HANDLE /*hSCM*/,
696 __in SC_HANDLE hService,
697 __in LPCWSTR wzServiceName,
698 __in DWORD dwRestartServiceDelayInSeconds,
699 __in LPCWSTR wzFirstFailureActionType,
700 __in LPCWSTR wzSecondFailureActionType,
701 __in LPCWSTR wzThirdFailureActionType,
702 __in DWORD dwResetPeriodInDays,
703 __in LPWSTR wzRebootMessage,
704 __in LPWSTR wzProgramCommandLine
705 )
706{
707 HRESULT hr = S_OK;
708 DWORD er = ERROR_SUCCESS;
709
710 HANDLE hToken = NULL;
711 TOKEN_PRIVILEGES priv = { 0 };
712 TOKEN_PRIVILEGES* pPrevPriv = NULL;
713 DWORD cbPrevPriv = 0;
714 BOOL fAdjustedPrivileges = FALSE;
715
716 SC_ACTION actions[3]; // the UI always shows 3 actions, so we'll always do 3
717 SERVICE_FAILURE_ACTIONSW sfa;
718 LPVOID lpMsgBuf = NULL;
719
720 // Always get the shutdown privilege in case we need to configure service to reboot.
721 if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &hToken))
722 {
723 ExitWithLastError(hr, "Failed to get process token.");
724 }
725
726 priv.PrivilegeCount = 1;
727 priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
728 if (!::LookupPrivilegeValueW(NULL, L"SeShutdownPrivilege", &priv.Privileges[0].Luid))
729 {
730 ExitWithLastError(hr, "Failed to get shutdown privilege LUID.");
731 }
732
733 cbPrevPriv = sizeof(TOKEN_PRIVILEGES);
734 pPrevPriv = static_cast<TOKEN_PRIVILEGES*>(MemAlloc(cbPrevPriv, TRUE));
735 ExitOnNull(pPrevPriv, hr, E_OUTOFMEMORY, "Failed to allocate memory for empty previous privileges.");
736
737 if (!::AdjustTokenPrivileges(hToken, FALSE, &priv, cbPrevPriv, pPrevPriv, &cbPrevPriv))
738 {
739 LPVOID pv = MemReAlloc(pPrevPriv, cbPrevPriv, TRUE);
740 ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for previous privileges.");
741 pPrevPriv = static_cast<TOKEN_PRIVILEGES*>(pv);
742
743 if (!::AdjustTokenPrivileges(hToken, FALSE, &priv, cbPrevPriv, pPrevPriv, &cbPrevPriv))
744 {
745 ExitWithLastError(hr, "Failed to get shutdown privilege LUID.");
746 }
747 }
748
749 fAdjustedPrivileges = TRUE;
750
751 // build up SC_ACTION array
752 // TODO: why is delay only respected when SC_ACTION_RESTART is requested?
753 actions[0].Type = GetSCActionType(wzFirstFailureActionType);
754 actions[0].Delay = 0;
755 if (SC_ACTION_RESTART == actions[0].Type)
756 {
757 actions[0].Delay = dwRestartServiceDelayInSeconds * 1000; // seconds to milliseconds
758 }
759
760 actions[1].Type = GetSCActionType(wzSecondFailureActionType);
761 actions[1].Delay = 0;
762 if (SC_ACTION_RESTART == actions[1].Type)
763 {
764 actions[1].Delay = dwRestartServiceDelayInSeconds * 1000; // seconds to milliseconds
765 }
766
767 actions[2].Type = GetSCActionType(wzThirdFailureActionType);
768 actions[2].Delay = 0;
769 if (SC_ACTION_RESTART == actions[2].Type)
770 {
771 actions[2].Delay = dwRestartServiceDelayInSeconds * 1000; // seconds to milliseconds
772 }
773
774 // build up the SERVICE_FAILURE_ACTIONSW struct
775 sfa.dwResetPeriod = dwResetPeriodInDays * (24 * 60 * 60); // days to seconds
776 sfa.lpRebootMsg = wzRebootMessage;
777 sfa.lpCommand = wzProgramCommandLine;
778 sfa.cActions = countof(actions);
779 sfa.lpsaActions = actions;
780
781 // Call ChangeServiceConfig2 to actually set up the failure actions
782 if (!::ChangeServiceConfig2W(hService, SERVICE_CONFIG_FAILURE_ACTIONS, (LPVOID)&sfa))
783 {
784 er = ::GetLastError();
785 hr = HRESULT_FROM_WIN32(er);
786
787#pragma prefast(push)
788#pragma prefast(disable:25028)
789 ::FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, er, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&lpMsgBuf, 0, NULL);
790#pragma prefast(pop)
791
792 // Check if this is a service that can't be modified.
793 if (ERROR_CANNOT_DETECT_PROCESS_ABORT == er)
794 {
795 WcaLog(LOGMSG_STANDARD, "WARNING: Service \"%ls\" is not configurable on this server and will not be set.", wzServiceName);
796 }
797 ExitOnFailure(hr, "Cannot change service configuration. Error: %ls", (LPWSTR)lpMsgBuf);
798
799 if (lpMsgBuf)
800 {
801 ::LocalFree(lpMsgBuf);
802 lpMsgBuf = NULL;
803 }
804 }
805
806LExit:
807 if (lpMsgBuf)
808 {
809 ::LocalFree(lpMsgBuf);
810 }
811
812 if (fAdjustedPrivileges)
813 {
814 ::AdjustTokenPrivileges(hToken, FALSE, pPrevPriv, 0, NULL, NULL);
815 }
816
817 ReleaseMem(pPrevPriv);
818 ReleaseHandle(hToken);
819
820 return hr;
821}
diff --git a/src/ext/Util/ca/shellexecca.cpp b/src/ext/Util/ca/shellexecca.cpp
new file mode 100644
index 00000000..ea21d3bd
--- /dev/null
+++ b/src/ext/Util/ca/shellexecca.cpp
@@ -0,0 +1,271 @@
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
5HRESULT ShellExec(
6 __in LPCWSTR wzTarget,
7 __in BOOL fUnelevated
8 )
9{
10 HRESULT hr = S_OK;
11 LPWSTR sczWorkingDirectory = NULL;
12
13 // a reasonable working directory (not the system32 default from MSI) is the directory where the target lives
14 hr = PathGetDirectory(wzTarget, &sczWorkingDirectory);
15 ExitOnFailure(hr, "failed to get directory for target: %ls", wzTarget);
16
17 if (!DirExists(sczWorkingDirectory, NULL))
18 {
19 ReleaseNullStr(sczWorkingDirectory);
20 }
21
22 if (fUnelevated)
23 {
24 hr = ShelExecUnelevated(wzTarget, NULL, NULL, sczWorkingDirectory, SW_SHOWDEFAULT);
25 ExitOnFailure(hr, "ShelExecUnelevated failed with target %ls", wzTarget);
26 }
27 else
28 {
29 HINSTANCE hinst = ::ShellExecuteW(NULL, NULL, wzTarget, NULL, sczWorkingDirectory, SW_SHOWDEFAULT);
30 if (hinst <= HINSTANCE(32))
31 {
32 LONG64 code = reinterpret_cast<LONG64>(hinst);
33 switch (code)
34 {
35 case ERROR_FILE_NOT_FOUND:
36 hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
37 break;
38 case ERROR_PATH_NOT_FOUND:
39 hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
40 break;
41 case ERROR_BAD_FORMAT:
42 hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT);
43 break;
44 case SE_ERR_ASSOCINCOMPLETE:
45 case SE_ERR_NOASSOC:
46 hr = HRESULT_FROM_WIN32(ERROR_NO_ASSOCIATION);
47 break;
48 case SE_ERR_DDEBUSY:
49 case SE_ERR_DDEFAIL:
50 case SE_ERR_DDETIMEOUT:
51 hr = HRESULT_FROM_WIN32(ERROR_DDE_FAIL);
52 break;
53 case SE_ERR_DLLNOTFOUND:
54 hr = HRESULT_FROM_WIN32(ERROR_DLL_NOT_FOUND);
55 break;
56 case SE_ERR_OOM:
57 hr = E_OUTOFMEMORY;
58 break;
59 case SE_ERR_ACCESSDENIED:
60 hr = E_ACCESSDENIED;
61 break;
62 default:
63 hr = E_FAIL;
64 }
65
66 ExitOnFailure(hr, "ShellExec failed with return code %llu.", code);
67 }
68 }
69
70
71LExit:
72 ReleaseStr(sczWorkingDirectory);
73 return hr;
74}
75
76extern "C" UINT __stdcall WixShellExec(
77 __in MSIHANDLE hInstall
78 )
79{
80 Assert(hInstall);
81 HRESULT hr = S_OK;
82 UINT er = ERROR_SUCCESS;
83 LPWSTR pwzTarget = NULL;
84
85 hr = WcaInitialize(hInstall, "WixShellExec");
86 ExitOnFailure(hr, "failed to initialize");
87
88 hr = WcaGetFormattedProperty(L"WixShellExecTarget", &pwzTarget);
89 ExitOnFailure(hr, "failed to get WixShellExecTarget");
90
91 WcaLog(LOGMSG_VERBOSE, "WixShellExecTarget is %ls", pwzTarget);
92
93 if (!pwzTarget || !*pwzTarget)
94 {
95 hr = E_INVALIDARG;
96 ExitOnFailure(hr, "failed to get WixShellExecTarget");
97 }
98
99 hr = ShellExec(pwzTarget, FALSE);
100 ExitOnFailure(hr, "failed to launch target");
101
102LExit:
103 ReleaseStr(pwzTarget);
104
105 if (FAILED(hr))
106 {
107 er = ERROR_INSTALL_FAILURE;
108 }
109 return WcaFinalize(er);
110}
111
112extern "C" UINT __stdcall WixUnelevatedShellExec(
113 __in MSIHANDLE hInstall
114 )
115{
116 Assert(hInstall);
117 HRESULT hr = S_OK;
118 UINT er = ERROR_SUCCESS;
119 LPWSTR pwzTarget = NULL;
120
121 hr = WcaInitialize(hInstall, "WixUnelevatedShellExec");
122 ExitOnFailure(hr, "failed to initialize");
123
124 hr = WcaGetFormattedProperty(L"WixUnelevatedShellExecTarget", &pwzTarget);
125 ExitOnFailure(hr, "failed to get WixUnelevatedShellExecTarget");
126
127 WcaLog(LOGMSG_VERBOSE, "WixUnelevatedShellExecTarget is %ls", pwzTarget);
128
129 if (!pwzTarget || !*pwzTarget)
130 {
131 hr = E_INVALIDARG;
132 ExitOnFailure(hr, "failed to get WixShellExecTarget");
133 }
134
135 hr = ShellExec(pwzTarget, TRUE);
136 ExitOnFailure(hr, "failed to launch target");
137
138LExit:
139 ReleaseStr(pwzTarget);
140
141 if (FAILED(hr))
142 {
143 er = ERROR_INSTALL_FAILURE;
144 }
145 return WcaFinalize(er);
146}
147
148//
149// ExtractBinary extracts the data from the Binary table row with the given ID into a file.
150//
151HRESULT ExtractBinary(
152 __in LPCWSTR wzBinaryId,
153 __out BYTE** pbData,
154 __out DWORD* pcbData
155 )
156{
157 HRESULT hr = S_OK;
158 LPWSTR pwzSql = NULL;
159 PMSIHANDLE hView;
160 PMSIHANDLE hRec;
161
162 // make sure we're not horked from the get-go
163 hr = WcaTableExists(L"Binary");
164 if (S_OK != hr)
165 {
166 if (SUCCEEDED(hr))
167 {
168 hr = E_UNEXPECTED;
169 }
170 ExitOnFailure(hr, "There is no Binary table.");
171 }
172
173 ExitOnNull(wzBinaryId, hr, E_INVALIDARG, "Binary ID cannot be null");
174 ExitOnNull(*wzBinaryId, hr, E_INVALIDARG, "Binary ID cannot be empty string");
175
176 hr = StrAllocFormatted(&pwzSql, L"SELECT `Data` FROM `Binary` WHERE `Name`=\'%s\'", wzBinaryId);
177 ExitOnFailure(hr, "Failed to allocate Binary table query.");
178
179 hr = WcaOpenExecuteView(pwzSql, &hView);
180 ExitOnFailure(hr, "Failed to open view on Binary table");
181
182 hr = WcaFetchSingleRecord(hView, &hRec);
183 ExitOnFailure(hr, "Failed to retrieve request from Binary table");
184
185 hr = WcaGetRecordStream(hRec, 1, pbData, pcbData);
186 ExitOnFailure(hr, "Failed to read Binary.Data.");
187
188LExit:
189 ReleaseStr(pwzSql);
190
191 return hr;
192}
193
194extern "C" UINT __stdcall WixShellExecBinary(
195 __in MSIHANDLE hInstall
196 )
197{
198 Assert(hInstall);
199 HRESULT hr = S_OK;
200 UINT er = ERROR_SUCCESS;
201 LPWSTR pwzBinary = NULL;
202 LPWSTR pwzFilename = NULL;
203 BYTE* pbData = NULL;
204 DWORD cbData = 0;
205 HANDLE hFile = INVALID_HANDLE_VALUE;
206
207#if 0
208 ::MessageBoxA(0, "WixShellExecBinary", "-->> ATTACH HERE", MB_OK);
209#endif
210
211 hr = WcaInitialize(hInstall, "WixShellExecBinary");
212 ExitOnFailure(hr, "failed to initialize");
213
214 hr = WcaGetFormattedProperty(L"WixShellExecBinaryId", &pwzBinary);
215 ExitOnFailure(hr, "failed to get WixShellExecBinaryId");
216
217 WcaLog(LOGMSG_VERBOSE, "WixShellExecBinaryId is %ls", pwzBinary);
218
219 if (!pwzBinary || !*pwzBinary)
220 {
221 hr = E_INVALIDARG;
222 ExitOnFailure(hr, "failed to get WixShellExecBinaryId");
223 }
224
225 // get temporary path for extracted file
226 StrAlloc(&pwzFilename, MAX_PATH);
227 ExitOnFailure(hr, "Failed to allocate temporary path");
228 ::GetTempPathW(MAX_PATH, pwzFilename);
229 hr = ::StringCchCatW(pwzFilename, MAX_PATH, pwzBinary);
230 ExitOnFailure(hr, "Failed to append filename.");
231
232 // grab the bits
233 hr = ExtractBinary(pwzBinary, &pbData, &cbData);
234 ExitOnFailure(hr, "failed to extract binary data");
235
236 // write 'em to the temp file
237 hFile = ::CreateFileW(pwzFilename, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
238 if (INVALID_HANDLE_VALUE == hFile)
239 {
240 ExitWithLastError(hr, "Failed to open new temp file: %ls", pwzFilename);
241 }
242
243 DWORD cbWritten = 0;
244 if (!::WriteFile(hFile, pbData, cbData, &cbWritten, NULL))
245 {
246 ExitWithLastError(hr, "Failed to write data to new temp file: %ls", pwzFilename);
247 }
248
249 // close it
250 ::CloseHandle(hFile);
251 hFile = INVALID_HANDLE_VALUE;
252
253 // and run it
254 hr = ShellExec(pwzFilename, FALSE);
255 ExitOnFailure(hr, "failed to launch target: %ls", pwzFilename);
256
257LExit:
258 ReleaseStr(pwzBinary);
259 ReleaseStr(pwzFilename);
260 ReleaseMem(pbData);
261 if (INVALID_HANDLE_VALUE != hFile)
262 {
263 ::CloseHandle(hFile);
264 }
265
266 if (FAILED(hr))
267 {
268 er = ERROR_INSTALL_FAILURE;
269 }
270 return WcaFinalize(er);
271}
diff --git a/src/ext/Util/ca/test.cpp b/src/ext/Util/ca/test.cpp
new file mode 100644
index 00000000..c4d215f0
--- /dev/null
+++ b/src/ext/Util/ca/test.cpp
@@ -0,0 +1,269 @@
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 WIXCA_UITHREAD_CLASS_WINDOW L"WixCaMessageWindow"
6
7extern HMODULE g_hInstCADLL;
8
9
10// structs
11
12struct UITHREAD_CONTEXT
13{
14 HANDLE hInitializedEvent;
15 HINSTANCE hInstance;
16 HWND hWnd;
17};
18
19
20// internal function declarations
21
22static HRESULT CreateMessageWindow(
23 __out HWND* phWnd
24 );
25
26static void CloseMessageWindow(
27 __in HWND hWnd
28 );
29
30static DWORD WINAPI ThreadProc(
31 __in LPVOID pvContext
32 );
33
34static LRESULT CALLBACK WndProc(
35 __in HWND hWnd,
36 __in UINT uMsg,
37 __in WPARAM wParam,
38 __in LPARAM lParam
39 );
40
41
42/******************************************************************
43WixFailWhenDeferred - entry point for WixFailWhenDeferred
44 custom action which always fails when running as a deferred
45 custom action (otherwise it blindly succeeds). It's useful when
46 testing the rollback of deferred custom actions: Schedule it
47 immediately after the rollback/deferred CA pair you're testing
48 and it will fail, causing your rollback CA to get invoked.
49********************************************************************/
50extern "C" UINT __stdcall WixFailWhenDeferred(
51 __in MSIHANDLE hInstall
52 )
53{
54 return ::MsiGetMode(hInstall, MSIRUNMODE_SCHEDULED) ? ERROR_INSTALL_FAILURE : ERROR_SUCCESS;
55}
56
57/******************************************************************
58WixWaitForEvent - entry point for WixWaitForEvent custom action
59 which waits for either the WixWaitForEventFail or
60 WixWaitForEventSucceed named auto reset events. Signaling the
61 WixWaitForEventFail event will return ERROR_INSTALL_FAILURE or
62 signaling the WixWaitForEventSucceed event will return
63 ERROR_SUCCESS. Both events are declared in the Global\ namespace.
64********************************************************************/
65extern "C" UINT __stdcall WixWaitForEvent(
66 __in MSIHANDLE hInstall
67 )
68{
69 HRESULT hr = S_OK;
70 UINT er = ERROR_SUCCESS;
71 HWND hMessageWindow = NULL;
72 LPCWSTR wzSDDL = L"D:(A;;GA;;;WD)";
73 OS_VERSION version = OS_VERSION_UNKNOWN;
74 DWORD dwServicePack = 0;
75 PSECURITY_DESCRIPTOR pSD = NULL;
76 SECURITY_ATTRIBUTES sa = { };
77 HANDLE rghEvents[2];
78
79 hr = WcaInitialize(hInstall, "WixWaitForEvent");
80 ExitOnFailure(hr, "Failed to initialize.");
81
82 // Create a window to prevent shutdown requests.
83 hr = CreateMessageWindow(&hMessageWindow);
84 ExitOnFailure(hr, "Failed to create message window.");
85
86 // If running on Vista/2008 or newer use integrity enhancements.
87 OsGetVersion(&version, &dwServicePack);
88 if (OS_VERSION_VISTA <= version)
89 {
90 // Add SACL to allow Everyone to signal from a medium integrity level.
91 wzSDDL = L"D:(A;;GA;;;WD)S:(ML;;NW;;;ME)";
92 }
93
94 // Create the security descriptor and attributes for the events.
95 if (!::ConvertStringSecurityDescriptorToSecurityDescriptorW(wzSDDL, SDDL_REVISION_1, &pSD, NULL))
96 {
97 ExitWithLastError(hr, "Failed to create the security descriptor for the events.");
98 }
99
100 sa.nLength = sizeof(sa);
101 sa.lpSecurityDescriptor = pSD;
102 sa.bInheritHandle = FALSE;
103
104 rghEvents[0] = ::CreateEventW(&sa, FALSE, FALSE, L"Global\\WixWaitForEventFail");
105 ExitOnNullWithLastError(rghEvents[0], hr, "Failed to create the Global\\WixWaitForEventFail event.");
106
107 rghEvents[1] = ::CreateEventW(&sa, FALSE, FALSE, L"Global\\WixWaitForEventSucceed");
108 ExitOnNullWithLastError(rghEvents[1], hr, "Failed to create the Global\\WixWaitForEventSucceed event.");
109
110 // Wait for either of the events to be signaled and handle accordingly.
111 er = ::WaitForMultipleObjects(countof(rghEvents), rghEvents, FALSE, INFINITE);
112 switch (er)
113 {
114 case WAIT_OBJECT_0 + 0:
115 er = ERROR_INSTALL_FAILURE;
116 break;
117 case WAIT_OBJECT_0 + 1:
118 er = ERROR_SUCCESS;
119 break;
120 default:
121 ExitOnWin32Error(er, hr, "Unexpected failure.");
122 }
123
124LExit:
125 ReleaseHandle(rghEvents[1]);
126 ReleaseHandle(rghEvents[0]);
127
128 if (pSD)
129 {
130 ::LocalFree(pSD);
131 }
132
133 if (hMessageWindow)
134 {
135 CloseMessageWindow(hMessageWindow);
136 }
137
138 if (FAILED(hr))
139 {
140 er = ERROR_INSTALL_FAILURE;
141 }
142
143 return WcaFinalize(er);
144}
145
146
147// internal function definitions
148
149static HRESULT CreateMessageWindow(
150 __out HWND* phWnd
151 )
152{
153 HRESULT hr = S_OK;
154 HANDLE rgWaitHandles[2] = { };
155 UITHREAD_CONTEXT context = { };
156
157 // Create event to signal after the UI thread / window is initialized.
158 rgWaitHandles[0] = ::CreateEventW(NULL, TRUE, FALSE, NULL);
159 ExitOnNullWithLastError(rgWaitHandles[0], hr, "Failed to create initialization event.");
160
161 // Pass necessary information to create the window.
162 context.hInitializedEvent = rgWaitHandles[0];
163 context.hInstance = (HINSTANCE)g_hInstCADLL;
164
165 // Create our separate UI thread.
166 rgWaitHandles[1] = ::CreateThread(NULL, 0, ThreadProc, &context, 0, NULL);
167 ExitOnNullWithLastError(rgWaitHandles[1], hr, "Failed to create the UI thread.");
168
169 // Wait for either the thread to be initialized or the window to exit / fail prematurely.
170 ::WaitForMultipleObjects(countof(rgWaitHandles), rgWaitHandles, FALSE, INFINITE);
171
172 // Pass the window back to the caller.
173 *phWnd = context.hWnd;
174
175LExit:
176 ReleaseHandle(rgWaitHandles[1]);
177 ReleaseHandle(rgWaitHandles[0]);
178
179 return hr;
180}
181
182static void CloseMessageWindow(
183 __in HWND hWnd
184 )
185{
186 if (::IsWindow(hWnd))
187 {
188 ::PostMessageW(hWnd, WM_CLOSE, 0, 0);
189 }
190}
191
192static DWORD WINAPI ThreadProc(
193 __in LPVOID pvContext
194 )
195{
196 HRESULT hr = S_OK;
197 UITHREAD_CONTEXT* pContext = static_cast<UITHREAD_CONTEXT*>(pvContext);
198
199 WNDCLASSW wc = { };
200 BOOL fRegistered = TRUE;
201 HWND hWnd = NULL;
202
203 BOOL fRet = FALSE;
204 MSG msg = { };
205
206 wc.lpfnWndProc = WndProc;
207 wc.hInstance = pContext->hInstance;
208 wc.lpszClassName = WIXCA_UITHREAD_CLASS_WINDOW;
209
210 if (!::RegisterClassW(&wc))
211 {
212 ExitWithLastError(hr, "Failed to register window.");
213 }
214
215 fRegistered = TRUE;
216
217 // Create the window to handle reboots without activating it.
218 hWnd = ::CreateWindowExW(WS_EX_TOOLWINDOW, wc.lpszClassName, NULL, WS_POPUP | WS_VISIBLE, CW_USEDEFAULT, SW_SHOWNA, 0, 0, HWND_DESKTOP, NULL, pContext->hInstance, NULL);
219 ExitOnNullWithLastError(hWnd, hr, "Failed to create window.");
220
221 // Persist the window handle and let the caller know we've initialized.
222 pContext->hWnd = hWnd;
223 ::SetEvent(pContext->hInitializedEvent);
224
225 // Pump messages until the window is closed.
226 while (0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0)))
227 {
228 if (-1 == fRet)
229 {
230 hr = E_UNEXPECTED;
231 ExitOnFailure(hr, "Unexpected return value from message pump.");
232 }
233 else if (!::IsDialogMessageW(msg.hwnd, &msg))
234 {
235 ::TranslateMessage(&msg);
236 ::DispatchMessageW(&msg);
237 }
238 }
239
240LExit:
241 if (fRegistered)
242 {
243 ::UnregisterClassW(WIXCA_UITHREAD_CLASS_WINDOW, pContext->hInstance);
244 }
245
246 return hr;
247}
248
249static LRESULT CALLBACK WndProc(
250 __in HWND hWnd,
251 __in UINT uMsg,
252 __in WPARAM wParam,
253 __in LPARAM lParam
254 )
255{
256 switch (uMsg)
257 {
258 case WM_QUERYENDSESSION:
259 // Prevent the process from being shut down.
260 WcaLog(LOGMSG_VERBOSE, "Disallowed system request to shut down the custom action server.");
261 return FALSE;
262
263 case WM_DESTROY:
264 ::PostQuitMessage(0);
265 return 0;
266 }
267
268 return ::DefWindowProcW(hWnd, uMsg, wParam, lParam);
269}
diff --git a/src/ext/Util/ca/utilca.cpp b/src/ext/Util/ca/utilca.cpp
new file mode 100644
index 00000000..37664a1c
--- /dev/null
+++ b/src/ext/Util/ca/utilca.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/ext/Util/ca/utilca.def b/src/ext/Util/ca/utilca.def
new file mode 100644
index 00000000..412d86a3
--- /dev/null
+++ b/src/ext/Util/ca/utilca.def
@@ -0,0 +1,91 @@
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
4LIBRARY "utilca"
5
6EXPORTS
7; BroadcastSettingChange.cpp
8 WixBroadcastSettingChange
9 WixBroadcastEnvironmentChange
10; checkreboot.cpp
11 WixCheckRebootRequired
12; closeapps.cpp
13 WixCloseApplications
14 WixCloseApplicationsDeferred
15; exitearlywithsuccess.cpp
16 WixExitEarlyWithSuccess
17; FormatFiles.cpp
18 WixSchedFormatFiles
19 WixExecFormatFiles
20; osinfo.cpp
21 WixQueryOsInfo
22 WixQueryOsDirs
23 WixQueryOsWellKnownSID
24 WixQueryOsDriverInfo
25; netshortcuts.cpp
26 WixSchedInternetShortcuts
27 WixCreateInternetShortcuts
28 WixRollbackInternetShortcuts
29; qtexecca.cpp
30 CAQuietExec
31 CAQuietExec64
32 WixQuietExec
33 WixQuietExec64
34 WixSilentExec
35 WixSilentExec64
36; RemoveFoldersEx.cpp
37 WixRemoveFoldersEx
38; RemoveRegistryKeysEx.cpp
39 WixRemoveRegistryKeysEx
40;scaexec.cpp
41 RegisterPerfCounterData
42 UnregisterPerfCounterData
43 RegisterPerfmon
44 UnregisterPerfmon
45 CreateSmb
46 DropSmb
47 CreateUser
48 CreateUserRollback
49 RemoveUser
50;scasched.cpp
51 ConfigurePerfmonInstall
52 ConfigurePerfmonUninstall
53 ConfigureSmbInstall
54 ConfigureSmbUninstall
55 ConfigureUsers
56 InstallPerfCounterData
57 UninstallPerfCounterData
58 ConfigurePerfmonManifestRegister
59 ConfigurePerfmonManifestUnregister
60 ConfigureEventManifestRegister
61 ConfigureEventManifestUnregister
62; RestartManager.cpp
63 WixRegisterRestartResources
64; secureobj.cpp
65 SchedSecureObjects
66 SchedSecureObjectsRollback
67 ExecSecureObjects
68 ExecSecureObjectsRollback
69; serviceconfig.cpp
70 SchedServiceConfig
71 ExecServiceConfig
72 RollbackServiceConfig
73; shellexecca.cpp
74 WixShellExec
75 WixShellExecBinary
76 WixUnelevatedShellExec
77; test.cpp
78 WixFailWhenDeferred
79 WixWaitForEvent
80; TouchFile.cpp
81 WixTouchFileDuringInstall
82 WixTouchFileDuringUninstall
83 WixExecuteTouchFile
84; xmlfile.cpp
85 SchedXmlFile
86 ExecXmlFile
87 ExecXmlFileRollback
88; xmlconfig.cpp
89 SchedXmlConfig
90 ExecXmlConfig
91 ExecXmlConfigRollback
diff --git a/src/ext/Util/ca/utilca.vcxproj b/src/ext/Util/ca/utilca.vcxproj
new file mode 100644
index 00000000..7b64db95
--- /dev/null
+++ b/src/ext/Util/ca/utilca.vcxproj
@@ -0,0 +1,106 @@
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="16.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
5 <ItemGroup Label="ProjectConfigurations">
6 <ProjectConfiguration Include="Debug|ARM64">
7 <Configuration>Debug</Configuration>
8 <Platform>ARM64</Platform>
9 </ProjectConfiguration>
10 <ProjectConfiguration Include="Release|ARM64">
11 <Configuration>Release</Configuration>
12 <Platform>ARM64</Platform>
13 </ProjectConfiguration>
14 <ProjectConfiguration Include="Debug|X64">
15 <Configuration>Debug</Configuration>
16 <Platform>X64</Platform>
17 </ProjectConfiguration>
18 <ProjectConfiguration Include="Release|X64">
19 <Configuration>Release</Configuration>
20 <Platform>X64</Platform>
21 </ProjectConfiguration>
22 <ProjectConfiguration Include="Debug|Win32">
23 <Configuration>Debug</Configuration>
24 <Platform>Win32</Platform>
25 </ProjectConfiguration>
26 <ProjectConfiguration Include="Release|Win32">
27 <Configuration>Release</Configuration>
28 <Platform>Win32</Platform>
29 </ProjectConfiguration>
30 </ItemGroup>
31
32 <PropertyGroup Label="Globals">
33 <ProjectGuid>{076018F7-19BD-423A-ABBF-229273DA08D8}</ProjectGuid>
34 <ConfigurationType>DynamicLibrary</ConfigurationType>
35 <TargetName>utilca</TargetName>
36 <PlatformToolset>v142</PlatformToolset>
37 <CharacterSet>Unicode</CharacterSet>
38 <ProjectModuleDefinitionFile>utilca.def</ProjectModuleDefinitionFile>
39 <Description>WiX Toolset Util CustomAction</Description>
40 </PropertyGroup>
41
42 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
43 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
44
45 <PropertyGroup>
46 <ProjectAdditionalLinkLibraries>activeds.lib;adsiid.lib;msi.lib;netapi32.lib;shlwapi.lib</ProjectAdditionalLinkLibraries>
47 </PropertyGroup>
48
49 <ItemGroup>
50 <ClCompile Include="BroadcastSettingChange.cpp" />
51 <ClCompile Include="CheckReboot.cpp" />
52 <ClCompile Include="CloseApps.cpp" />
53 <ClCompile Include="dllmain.cpp">
54 <PrecompiledHeader>Create</PrecompiledHeader>
55 </ClCompile>
56 <ClCompile Include="exitearlywithsuccess.cpp" />
57 <ClCompile Include="FormatFiles.cpp" />
58 <ClCompile Include="netshortcuts.cpp" />
59 <ClCompile Include="OsInfo.cpp" />
60 <ClCompile Include="qtexecca.cpp" />
61 <ClCompile Include="RemoveFoldersEx.cpp" />
62 <ClCompile Include="RemoveRegistryKeysEx.cpp" />
63 <ClCompile Include="RestartManager.cpp" />
64 <ClCompile Include="scaexec.cpp" />
65 <ClCompile Include="scamanifest.cpp" />
66 <ClCompile Include="scaperf.cpp" />
67 <ClCompile Include="scaperfexec.cpp" />
68 <ClCompile Include="scasched.cpp" />
69 <ClCompile Include="scasmbexec.cpp" />
70 <ClCompile Include="scasmbsched.cpp" />
71 <ClCompile Include="scauser.cpp" />
72 <ClCompile Include="secureobj.cpp" />
73 <ClCompile Include="serviceconfig.cpp" />
74 <ClCompile Include="shellexecca.cpp" />
75 <ClCompile Include="test.cpp" />
76 <ClCompile Include="TouchFile.cpp" />
77 <ClCompile Include="utilca.cpp" />
78 <ClCompile Include="XmlConfig.cpp" />
79 <ClCompile Include="XmlFile.cpp" />
80 </ItemGroup>
81
82 <ItemGroup>
83 <ClInclude Include="caDecor.h" />
84 <ClInclude Include="cost.h" />
85 <ClInclude Include="CustomMsiErrors.h" />
86 <ClInclude Include="precomp.h" />
87 <ClInclude Include="sca.h" />
88 <ClInclude Include="scacost.h" />
89 <ClInclude Include="scasmb.h" />
90 <ClInclude Include="scasmbexec.h" />
91 <ClInclude Include="scauser.h" />
92 </ItemGroup>
93
94 <ItemGroup>
95 <None Include="utilca.def" />
96 </ItemGroup>
97
98 <ItemGroup>
99 <PackageReference Include="WixToolset.Dutil" Version="4.0.72" />
100 <PackageReference Include="WixToolset.WcaUtil" Version="4.0.19" />
101 <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" />
102 <PackageReference Include="Nerdbank.GitVersioning" Version="3.3.37" />
103 </ItemGroup>
104
105 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
106</Project>
diff --git a/src/ext/Util/nuget.config b/src/ext/Util/nuget.config
new file mode 100644
index 00000000..8d711148
--- /dev/null
+++ b/src/ext/Util/nuget.config
@@ -0,0 +1,18 @@
1<?xml version="1.0" encoding="utf-8"?>
2<configuration>
3 <packageSources>
4 <clear />
5 <add key="wixtoolset-balutil" value="https://ci.appveyor.com/nuget/wixtoolset-balutil" />
6 <add key="wixtoolset-burn" value="https://ci.appveyor.com/nuget/wixtoolset-burn" />
7 <add key="wixtoolset-data" value="https://ci.appveyor.com/nuget/wixtoolset-data" />
8 <add key="wixtoolset-extensibility" value="https://ci.appveyor.com/nuget/wixtoolset-extensibility" />
9 <add key="wixtoolset-core" value="https://ci.appveyor.com/nuget/wixtoolset-core" />
10 <add key="wixtoolset-core-native" value="https://ci.appveyor.com/nuget/wixtoolset-core-native" />
11 <add key="wixtoolset-dtf" value="https://ci.appveyor.com/nuget/wixtoolset-dtf" />
12 <add key="wixtoolset-dutil" value="https://ci.appveyor.com/nuget/wixtoolset-dutil" />
13 <add key="wixtoolset-wcautil" value="https://ci.appveyor.com/nuget/wixtoolset-wcautil" />
14 <add key="wixtoolset-tools" value="https://ci.appveyor.com/nuget/wixtoolset-tools" />
15 <add key="wixbuildtools" value="https://ci.appveyor.com/nuget/wixbuildtools" />
16 <add key="api.nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
17 </packageSources>
18</configuration> \ No newline at end of file
diff --git a/src/ext/Util/test/WixToolsetTest.Util/TestData/.Data/burn.exe b/src/ext/Util/test/WixToolsetTest.Util/TestData/.Data/burn.exe
new file mode 100644
index 00000000..2a4f423f
--- /dev/null
+++ b/src/ext/Util/test/WixToolsetTest.Util/TestData/.Data/burn.exe
Binary files differ
diff --git a/src/ext/Util/test/WixToolsetTest.Util/TestData/BundleWithSearches/Bundle.en-us.wxl b/src/ext/Util/test/WixToolsetTest.Util/TestData/BundleWithSearches/Bundle.en-us.wxl
new file mode 100644
index 00000000..f50a5386
--- /dev/null
+++ b/src/ext/Util/test/WixToolsetTest.Util/TestData/BundleWithSearches/Bundle.en-us.wxl
@@ -0,0 +1,8 @@
1<!--
2This file contains the declaration of all the localizable strings.
3-->
4<WixLocalization xmlns="http://wixtoolset.org/schemas/v4/wxl" Culture="en-US">
5
6 <String Id="BundleName">~TestBundle</String>
7
8</WixLocalization>
diff --git a/src/ext/Util/test/WixToolsetTest.Util/TestData/BundleWithSearches/Bundle.wxs b/src/ext/Util/test/WixToolsetTest.Util/TestData/BundleWithSearches/Bundle.wxs
new file mode 100644
index 00000000..7fef0725
--- /dev/null
+++ b/src/ext/Util/test/WixToolsetTest.Util/TestData/BundleWithSearches/Bundle.wxs
@@ -0,0 +1,52 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:util="http://wixtoolset.org/schemas/v4/wxs/util">
2 <Bundle Name="!(loc.BundleName)" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="047730a5-30fe-4a62-a520-da9381b8226a">
3 <BootstrapperApplication>
4 <BootstrapperApplicationDll SourceFile="fakeba.dll" />
5 </BootstrapperApplication>
6
7 <util:RegistrySearchRef Id="RegistrySearchId" />
8 <util:RegistrySearchRef Id="RegistrySearchId64" />
9 <util:ProductSearchRef Id="ProductSearchId" />
10 <util:FileSearchRef Id="FileSearchId" />
11 <util:WindowsFeatureSearchRef Id="DetectSHA2SupportId" />
12
13 <Chain>
14 <MsiPackage SourceFile="test.msi">
15 <MsiProperty Name="TEST" Value="1" />
16 </MsiPackage>
17 </Chain>
18 </Bundle>
19
20 <Fragment>
21 <util:RegistrySearch
22 Id="RegistrySearchId"
23 Variable="RegistrySearchVariable"
24 Root="HKLM"
25 Key="SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full"
26 Value="Release"
27 Result="value" />
28 </Fragment>
29
30 <Fragment>
31 <util:RegistrySearch
32 Id="RegistrySearchId64"
33 Variable="RegistrySearchVariable64"
34 Root="HKLM"
35 Key="SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full"
36 Value="Release"
37 Result="value"
38 Bitness="always64" />
39 </Fragment>
40
41 <Fragment>
42 <util:ProductSearch Id="ProductSearchId" Variable="ProductSearchVariable" UpgradeCode="738D02BF-E231-4370-8209-E9FD4E1BE2A1" Condition="1 &amp; 2 &lt; 3" Result="version" />
43 </Fragment>
44
45 <Fragment>
46 <util:FileSearch Id="FileSearchId" Variable="FileSearchVariable" Path="%windir%\System32\mscoree.dll" Result="exists" />
47 </Fragment>
48
49 <Fragment>
50 <util:WindowsFeatureSearch Id="DetectSHA2SupportId" Variable="IsSHA2Supported" Feature="sha2CodeSigning" />
51 </Fragment>
52</Wix>
diff --git a/src/ext/Util/test/WixToolsetTest.Util/TestData/BundleWithSearches/data/MsiPackage/Shared.dll b/src/ext/Util/test/WixToolsetTest.Util/TestData/BundleWithSearches/data/MsiPackage/Shared.dll
new file mode 100644
index 00000000..0e461ba8
--- /dev/null
+++ b/src/ext/Util/test/WixToolsetTest.Util/TestData/BundleWithSearches/data/MsiPackage/Shared.dll
@@ -0,0 +1 @@
This is Shared.dll. \ No newline at end of file
diff --git a/src/ext/Util/test/WixToolsetTest.Util/TestData/BundleWithSearches/data/MsiPackage/test.txt b/src/ext/Util/test/WixToolsetTest.Util/TestData/BundleWithSearches/data/MsiPackage/test.txt
new file mode 100644
index 00000000..8b986220
--- /dev/null
+++ b/src/ext/Util/test/WixToolsetTest.Util/TestData/BundleWithSearches/data/MsiPackage/test.txt
@@ -0,0 +1 @@
This is test.txt \ No newline at end of file
diff --git a/src/ext/Util/test/WixToolsetTest.Util/TestData/BundleWithSearches/data/fakeba.dll b/src/ext/Util/test/WixToolsetTest.Util/TestData/BundleWithSearches/data/fakeba.dll
new file mode 100644
index 00000000..970efdf0
--- /dev/null
+++ b/src/ext/Util/test/WixToolsetTest.Util/TestData/BundleWithSearches/data/fakeba.dll
@@ -0,0 +1 @@
This is a fakeba.dll \ No newline at end of file
diff --git a/src/ext/Util/test/WixToolsetTest.Util/TestData/BundleWithSearches/data/test.msi b/src/ext/Util/test/WixToolsetTest.Util/TestData/BundleWithSearches/data/test.msi
new file mode 100644
index 00000000..0722d60e
--- /dev/null
+++ b/src/ext/Util/test/WixToolsetTest.Util/TestData/BundleWithSearches/data/test.msi
Binary files differ
diff --git a/src/ext/Util/test/WixToolsetTest.Util/TestData/CloseApplication/Package.en-us.wxl b/src/ext/Util/test/WixToolsetTest.Util/TestData/CloseApplication/Package.en-us.wxl
new file mode 100644
index 00000000..5301bb1a
--- /dev/null
+++ b/src/ext/Util/test/WixToolsetTest.Util/TestData/CloseApplication/Package.en-us.wxl
@@ -0,0 +1,9 @@
1<!--
2This file contains the declaration of all the localizable strings.
3-->
4<WixLocalization xmlns="http://wixtoolset.org/schemas/v4/wxl" Culture="en-US">
5
6 <String Id="DowngradeError">A newer version of [ProductName] is already installed.</String>
7 <String Id="FeatureTitle">MsiPackage</String>
8
9</WixLocalization>
diff --git a/src/ext/Util/test/WixToolsetTest.Util/TestData/CloseApplication/Package.wxs b/src/ext/Util/test/WixToolsetTest.Util/TestData/CloseApplication/Package.wxs
new file mode 100644
index 00000000..8e054256
--- /dev/null
+++ b/src/ext/Util/test/WixToolsetTest.Util/TestData/CloseApplication/Package.wxs
@@ -0,0 +1,17 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:util="http://wixtoolset.org/schemas/v4/wxs/util">
2 <Package Name="MsiPackage" Language="1033" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="047730a5-30fe-4a62-a520-da9381b8226a">
3 <MajorUpgrade DowngradeErrorMessage="!(loc.DowngradeError)" />
4
5 <Feature Id="ProductFeature" Title="!(loc.FeatureTitle)">
6 <ComponentGroupRef Id="ProductComponents" />
7 </Feature>
8
9 <util:CloseApplication Id="CloseMyApp" CloseMessage="yes" Property="MYAPPISRUNNING" Target="explorer.exe" />
10 </Package>
11
12 <Fragment>
13 <StandardDirectory Id="ProgramFilesFolder">
14 <Directory Id="INSTALLFOLDER" Name="MsiPackage" />
15 </StandardDirectory>
16 </Fragment>
17</Wix>
diff --git a/src/ext/Util/test/WixToolsetTest.Util/TestData/CloseApplication/PackageComponents.wxs b/src/ext/Util/test/WixToolsetTest.Util/TestData/CloseApplication/PackageComponents.wxs
new file mode 100644
index 00000000..e27b3c43
--- /dev/null
+++ b/src/ext/Util/test/WixToolsetTest.Util/TestData/CloseApplication/PackageComponents.wxs
@@ -0,0 +1,9 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 <Fragment>
3 <ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
4 <Component>
5 <File Source="example.txt" />
6 </Component>
7 </ComponentGroup>
8 </Fragment>
9</Wix>
diff --git a/src/ext/Util/test/WixToolsetTest.Util/TestData/CloseApplication/example.txt b/src/ext/Util/test/WixToolsetTest.Util/TestData/CloseApplication/example.txt
new file mode 100644
index 00000000..1b4ffe8a
--- /dev/null
+++ b/src/ext/Util/test/WixToolsetTest.Util/TestData/CloseApplication/example.txt
@@ -0,0 +1 @@
This is example.txt. \ No newline at end of file
diff --git a/src/ext/Util/test/WixToolsetTest.Util/TestData/EventManifest/Package.en-us.wxl b/src/ext/Util/test/WixToolsetTest.Util/TestData/EventManifest/Package.en-us.wxl
new file mode 100644
index 00000000..5301bb1a
--- /dev/null
+++ b/src/ext/Util/test/WixToolsetTest.Util/TestData/EventManifest/Package.en-us.wxl
@@ -0,0 +1,9 @@
1<!--
2This file contains the declaration of all the localizable strings.
3-->
4<WixLocalization xmlns="http://wixtoolset.org/schemas/v4/wxl" Culture="en-US">
5
6 <String Id="DowngradeError">A newer version of [ProductName] is already installed.</String>
7 <String Id="FeatureTitle">MsiPackage</String>
8
9</WixLocalization>
diff --git a/src/ext/Util/test/WixToolsetTest.Util/TestData/EventManifest/Package.wxs b/src/ext/Util/test/WixToolsetTest.Util/TestData/EventManifest/Package.wxs
new file mode 100644
index 00000000..daae573a
--- /dev/null
+++ b/src/ext/Util/test/WixToolsetTest.Util/TestData/EventManifest/Package.wxs
@@ -0,0 +1,15 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 <Package Name="MsiPackage" Language="1033" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="047730a5-30fe-4a62-a520-da9381b8226a">
3 <MajorUpgrade DowngradeErrorMessage="!(loc.DowngradeError)" />
4
5 <Feature Id="ProductFeature" Title="!(loc.FeatureTitle)">
6 <ComponentGroupRef Id="ProductComponents" />
7 </Feature>
8 </Package>
9
10 <Fragment>
11 <StandardDirectory Id="ProgramFilesFolder">
12 <Directory Id="INSTALLFOLDER" Name="MsiPackage" />
13 </StandardDirectory>
14 </Fragment>
15</Wix>
diff --git a/src/ext/Util/test/WixToolsetTest.Util/TestData/EventManifest/PackageComponents.wxs b/src/ext/Util/test/WixToolsetTest.Util/TestData/EventManifest/PackageComponents.wxs
new file mode 100644
index 00000000..2ec8ce82
--- /dev/null
+++ b/src/ext/Util/test/WixToolsetTest.Util/TestData/EventManifest/PackageComponents.wxs
@@ -0,0 +1,11 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:util="http://wixtoolset.org/schemas/v4/wxs/util">
2 <Fragment>
3 <ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
4 <Component>
5 <File Id="Manifest.dll" Source="example.txt">
6 <util:EventManifest MessageFile="[Manifest.dll]" ResourceFile="[Manifest.dll]" />
7 </File>
8 </Component>
9 </ComponentGroup>
10 </Fragment>
11</Wix>
diff --git a/src/ext/Util/test/WixToolsetTest.Util/TestData/EventManifest/example.txt b/src/ext/Util/test/WixToolsetTest.Util/TestData/EventManifest/example.txt
new file mode 100644
index 00000000..1b4ffe8a
--- /dev/null
+++ b/src/ext/Util/test/WixToolsetTest.Util/TestData/EventManifest/example.txt
@@ -0,0 +1 @@
This is example.txt. \ No newline at end of file
diff --git a/src/ext/Util/test/WixToolsetTest.Util/TestData/InternetShortcut/Package.en-us.wxl b/src/ext/Util/test/WixToolsetTest.Util/TestData/InternetShortcut/Package.en-us.wxl
new file mode 100644
index 00000000..5301bb1a
--- /dev/null
+++ b/src/ext/Util/test/WixToolsetTest.Util/TestData/InternetShortcut/Package.en-us.wxl
@@ -0,0 +1,9 @@
1<!--
2This file contains the declaration of all the localizable strings.
3-->
4<WixLocalization xmlns="http://wixtoolset.org/schemas/v4/wxl" Culture="en-US">
5
6 <String Id="DowngradeError">A newer version of [ProductName] is already installed.</String>
7 <String Id="FeatureTitle">MsiPackage</String>
8
9</WixLocalization>
diff --git a/src/ext/Util/test/WixToolsetTest.Util/TestData/InternetShortcut/Package.ico b/src/ext/Util/test/WixToolsetTest.Util/TestData/InternetShortcut/Package.ico
new file mode 100644
index 00000000..53134de7
--- /dev/null
+++ b/src/ext/Util/test/WixToolsetTest.Util/TestData/InternetShortcut/Package.ico
Binary files differ
diff --git a/src/ext/Util/test/WixToolsetTest.Util/TestData/InternetShortcut/Package.wxs b/src/ext/Util/test/WixToolsetTest.Util/TestData/InternetShortcut/Package.wxs
new file mode 100644
index 00000000..daae573a
--- /dev/null
+++ b/src/ext/Util/test/WixToolsetTest.Util/TestData/InternetShortcut/Package.wxs
@@ -0,0 +1,15 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 <Package Name="MsiPackage" Language="1033" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="047730a5-30fe-4a62-a520-da9381b8226a">
3 <MajorUpgrade DowngradeErrorMessage="!(loc.DowngradeError)" />
4
5 <Feature Id="ProductFeature" Title="!(loc.FeatureTitle)">
6 <ComponentGroupRef Id="ProductComponents" />
7 </Feature>
8 </Package>
9
10 <Fragment>
11 <StandardDirectory Id="ProgramFilesFolder">
12 <Directory Id="INSTALLFOLDER" Name="MsiPackage" />
13 </StandardDirectory>
14 </Fragment>
15</Wix>
diff --git a/src/ext/Util/test/WixToolsetTest.Util/TestData/InternetShortcut/PackageComponents.wxs b/src/ext/Util/test/WixToolsetTest.Util/TestData/InternetShortcut/PackageComponents.wxs
new file mode 100644
index 00000000..2a1b4347
--- /dev/null
+++ b/src/ext/Util/test/WixToolsetTest.Util/TestData/InternetShortcut/PackageComponents.wxs
@@ -0,0 +1,11 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:util="http://wixtoolset.org/schemas/v4/wxs/util">
2 <Fragment>
3 <ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
4 <Component>
5 <File Id="Package.ico" Source="Package.ico" />
6 <util:InternetShortcut Type="link" Name="WiX Toolset (link)" Target="https://wixtoolset.org" IconFile="[#Package.ico]" />
7 <util:InternetShortcut Type="url" Name="WiX Toolset (url)" Target="https://wixtoolset.org" IconFile="[#Package.ico]" />
8 </Component>
9 </ComponentGroup>
10 </Fragment>
11</Wix>
diff --git a/src/ext/Util/test/WixToolsetTest.Util/TestData/InternetShortcut/example.txt b/src/ext/Util/test/WixToolsetTest.Util/TestData/InternetShortcut/example.txt
new file mode 100644
index 00000000..1b4ffe8a
--- /dev/null
+++ b/src/ext/Util/test/WixToolsetTest.Util/TestData/InternetShortcut/example.txt
@@ -0,0 +1 @@
This is example.txt. \ No newline at end of file
diff --git a/src/ext/Util/test/WixToolsetTest.Util/TestData/InternetShortcutModule/Module.wxs b/src/ext/Util/test/WixToolsetTest.Util/TestData/InternetShortcutModule/Module.wxs
new file mode 100644
index 00000000..1355d42e
--- /dev/null
+++ b/src/ext/Util/test/WixToolsetTest.Util/TestData/InternetShortcutModule/Module.wxs
@@ -0,0 +1,13 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 <Module Language="1033" Version="1.0.0.0" Id="InternetShortcutModule" Guid="047730a5-30fe-4a62-a520-da9381b8226a">
3 <SummaryInformation Manufacturer="Example Corporation" />
4
5 <ComponentGroupRef Id="ProductComponents" />
6 </Module>
7
8 <Fragment>
9 <StandardDirectory Id="ProgramFilesFolder">
10 <Directory Id="INSTALLFOLDER" Name="MergeModule" />
11 </StandardDirectory>
12 </Fragment>
13</Wix>
diff --git a/src/ext/Util/test/WixToolsetTest.Util/TestData/InternetShortcutModule/ModuleComponents.wxs b/src/ext/Util/test/WixToolsetTest.Util/TestData/InternetShortcutModule/ModuleComponents.wxs
new file mode 100644
index 00000000..2a1b4347
--- /dev/null
+++ b/src/ext/Util/test/WixToolsetTest.Util/TestData/InternetShortcutModule/ModuleComponents.wxs
@@ -0,0 +1,11 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:util="http://wixtoolset.org/schemas/v4/wxs/util">
2 <Fragment>
3 <ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
4 <Component>
5 <File Id="Package.ico" Source="Package.ico" />
6 <util:InternetShortcut Type="link" Name="WiX Toolset (link)" Target="https://wixtoolset.org" IconFile="[#Package.ico]" />
7 <util:InternetShortcut Type="url" Name="WiX Toolset (url)" Target="https://wixtoolset.org" IconFile="[#Package.ico]" />
8 </Component>
9 </ComponentGroup>
10 </Fragment>
11</Wix>
diff --git a/src/ext/Util/test/WixToolsetTest.Util/TestData/InternetShortcutModule/Package.ico b/src/ext/Util/test/WixToolsetTest.Util/TestData/InternetShortcutModule/Package.ico
new file mode 100644
index 00000000..53134de7
--- /dev/null
+++ b/src/ext/Util/test/WixToolsetTest.Util/TestData/InternetShortcutModule/Package.ico
Binary files differ
diff --git a/src/ext/Util/test/WixToolsetTest.Util/TestData/PermissionEx/Package.en-us.wxl b/src/ext/Util/test/WixToolsetTest.Util/TestData/PermissionEx/Package.en-us.wxl
new file mode 100644
index 00000000..5301bb1a
--- /dev/null
+++ b/src/ext/Util/test/WixToolsetTest.Util/TestData/PermissionEx/Package.en-us.wxl
@@ -0,0 +1,9 @@
1<!--
2This file contains the declaration of all the localizable strings.
3-->
4<WixLocalization xmlns="http://wixtoolset.org/schemas/v4/wxl" Culture="en-US">
5
6 <String Id="DowngradeError">A newer version of [ProductName] is already installed.</String>
7 <String Id="FeatureTitle">MsiPackage</String>
8
9</WixLocalization>
diff --git a/src/ext/Util/test/WixToolsetTest.Util/TestData/PermissionEx/Package.wxs b/src/ext/Util/test/WixToolsetTest.Util/TestData/PermissionEx/Package.wxs
new file mode 100644
index 00000000..daae573a
--- /dev/null
+++ b/src/ext/Util/test/WixToolsetTest.Util/TestData/PermissionEx/Package.wxs
@@ -0,0 +1,15 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 <Package Name="MsiPackage" Language="1033" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="047730a5-30fe-4a62-a520-da9381b8226a">
3 <MajorUpgrade DowngradeErrorMessage="!(loc.DowngradeError)" />
4
5 <Feature Id="ProductFeature" Title="!(loc.FeatureTitle)">
6 <ComponentGroupRef Id="ProductComponents" />
7 </Feature>
8 </Package>
9
10 <Fragment>
11 <StandardDirectory Id="ProgramFilesFolder">
12 <Directory Id="INSTALLFOLDER" Name="MsiPackage" />
13 </StandardDirectory>
14 </Fragment>
15</Wix>
diff --git a/src/ext/Util/test/WixToolsetTest.Util/TestData/PermissionEx/PackageComponents.wxs b/src/ext/Util/test/WixToolsetTest.Util/TestData/PermissionEx/PackageComponents.wxs
new file mode 100644
index 00000000..0634d7d4
--- /dev/null
+++ b/src/ext/Util/test/WixToolsetTest.Util/TestData/PermissionEx/PackageComponents.wxs
@@ -0,0 +1,23 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:util="http://wixtoolset.org/schemas/v4/wxs/util">
2 <Fragment>
3 <ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
4 <Component>
5 <File Source="example.txt">
6 <util:PermissionEx User="Everyone" GenericAll="yes" />
7 </File>
8 <CreateFolder>
9 <util:PermissionEx User="Everyone" GenericAll="yes" />
10 </CreateFolder>
11 <ServiceInstall Name="testsvc" Type="ownProcess" Start="disabled" ErrorControl="normal">
12 <util:PermissionEx User="Everyone" GenericAll="yes" />
13 </ServiceInstall>
14 <RegistryKey Id="ExampleRegistryKey" ForceCreateOnInstall="yes" Root="HKLM" Key="TestRegistryKey">
15 <util:PermissionEx User="Everyone" GenericAll="yes" />
16 </RegistryKey>
17 <RegistryValue Root="HKLM" Key="TestRegistryValueKey" Value="abc">
18 <util:PermissionEx User="Everyone" GenericAll="yes" />
19 </RegistryValue>
20 </Component>
21 </ComponentGroup>
22 </Fragment>
23</Wix>
diff --git a/src/ext/Util/test/WixToolsetTest.Util/TestData/PermissionEx/example.txt b/src/ext/Util/test/WixToolsetTest.Util/TestData/PermissionEx/example.txt
new file mode 100644
index 00000000..1b4ffe8a
--- /dev/null
+++ b/src/ext/Util/test/WixToolsetTest.Util/TestData/PermissionEx/example.txt
@@ -0,0 +1 @@
This is example.txt. \ No newline at end of file
diff --git a/src/ext/Util/test/WixToolsetTest.Util/TestData/Queries/Package.en-us.wxl b/src/ext/Util/test/WixToolsetTest.Util/TestData/Queries/Package.en-us.wxl
new file mode 100644
index 00000000..5301bb1a
--- /dev/null
+++ b/src/ext/Util/test/WixToolsetTest.Util/TestData/Queries/Package.en-us.wxl
@@ -0,0 +1,9 @@
1<!--
2This file contains the declaration of all the localizable strings.
3-->
4<WixLocalization xmlns="http://wixtoolset.org/schemas/v4/wxl" Culture="en-US">
5
6 <String Id="DowngradeError">A newer version of [ProductName] is already installed.</String>
7 <String Id="FeatureTitle">MsiPackage</String>
8
9</WixLocalization>
diff --git a/src/ext/Util/test/WixToolsetTest.Util/TestData/Queries/Package.wxs b/src/ext/Util/test/WixToolsetTest.Util/TestData/Queries/Package.wxs
new file mode 100644
index 00000000..abf0dbb4
--- /dev/null
+++ b/src/ext/Util/test/WixToolsetTest.Util/TestData/Queries/Package.wxs
@@ -0,0 +1,23 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:util="http://wixtoolset.org/schemas/v4/wxs/util">
2 <Package Name="MsiPackage" Language="1033" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="047730a5-30fe-4a62-a520-da9381b8226a">
3 <MajorUpgrade DowngradeErrorMessage="!(loc.DowngradeError)" />
4
5 <util:BroadcastEnvironmentChange />
6 <util:CheckRebootRequired />
7 <util:QueryWindowsDriverInfo />
8 <util:QueryWindowsSuiteInfo />
9
10 <Feature Id="ProductFeature" Title="!(loc.FeatureTitle)">
11 <ComponentGroupRef Id="ProductComponents" />
12 </Feature>
13 </Package>
14
15 <Fragment><util:BroadcastSettingChange />
16
17
18
19 <StandardDirectory Id="ProgramFilesFolder">
20 <Directory Id="INSTALLFOLDER" Name="MsiPackage" />
21 </StandardDirectory>
22 </Fragment>
23</Wix>
diff --git a/src/ext/Util/test/WixToolsetTest.Util/TestData/Queries/PackageComponents.wxs b/src/ext/Util/test/WixToolsetTest.Util/TestData/Queries/PackageComponents.wxs
new file mode 100644
index 00000000..e27b3c43
--- /dev/null
+++ b/src/ext/Util/test/WixToolsetTest.Util/TestData/Queries/PackageComponents.wxs
@@ -0,0 +1,9 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 <Fragment>
3 <ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
4 <Component>
5 <File Source="example.txt" />
6 </Component>
7 </ComponentGroup>
8 </Fragment>
9</Wix>
diff --git a/src/ext/Util/test/WixToolsetTest.Util/TestData/Queries/example.txt b/src/ext/Util/test/WixToolsetTest.Util/TestData/Queries/example.txt
new file mode 100644
index 00000000..1b4ffe8a
--- /dev/null
+++ b/src/ext/Util/test/WixToolsetTest.Util/TestData/Queries/example.txt
@@ -0,0 +1 @@
This is example.txt. \ No newline at end of file
diff --git a/src/ext/Util/test/WixToolsetTest.Util/TestData/RemoveFolderEx/Module.wxs b/src/ext/Util/test/WixToolsetTest.Util/TestData/RemoveFolderEx/Module.wxs
new file mode 100644
index 00000000..2c2be584
--- /dev/null
+++ b/src/ext/Util/test/WixToolsetTest.Util/TestData/RemoveFolderEx/Module.wxs
@@ -0,0 +1,13 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 <Module Language="1033" Version="1.0.0.0" Id="InternetShortcutModule" Guid="047730a5-30fe-4a62-a520-da9381b8226a">
3 <SummaryInformation Manufacturer="Example Corporation" />
4
5 <ComponentGroupRef Id="ModuleComponents" />
6 </Module>
7
8 <Fragment>
9 <StandardDirectory Id="ProgramFilesFolder">
10 <Directory Id="INSTALLFOLDER" Name="MergeModule" />
11 </StandardDirectory>
12 </Fragment>
13</Wix>
diff --git a/src/ext/Util/test/WixToolsetTest.Util/TestData/RemoveFolderEx/ModuleComponents.wxs b/src/ext/Util/test/WixToolsetTest.Util/TestData/RemoveFolderEx/ModuleComponents.wxs
new file mode 100644
index 00000000..236d9df0
--- /dev/null
+++ b/src/ext/Util/test/WixToolsetTest.Util/TestData/RemoveFolderEx/ModuleComponents.wxs
@@ -0,0 +1,10 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:util="http://wixtoolset.org/schemas/v4/wxs/util">
2 <Fragment>
3 <ComponentGroup Id="ModuleComponents" Directory="INSTALLFOLDER">
4 <Component>
5 <File Source="ModuleComponents.wxs" />
6 <util:RemoveFolderEx On="both" Property="RemoveProp" />
7 </Component>
8 </ComponentGroup>
9 </Fragment>
10</Wix>
diff --git a/src/ext/Util/test/WixToolsetTest.Util/TestData/RemoveRegistryKeyEx/Module.wxs b/src/ext/Util/test/WixToolsetTest.Util/TestData/RemoveRegistryKeyEx/Module.wxs
new file mode 100644
index 00000000..32b246f4
--- /dev/null
+++ b/src/ext/Util/test/WixToolsetTest.Util/TestData/RemoveRegistryKeyEx/Module.wxs
@@ -0,0 +1,13 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 <Module Language="1033" Version="1.0.0.0" Id="InternetShortcutModule" Guid="047730a5-30fe-4a62-a520-da9381b8226a">
3 <SummaryInformation Manufacturer="Example Corporation" />
4
5 <ComponentGroupRef Id="ModuleComponents" />
6 </Module>
7
8 <Fragment>
9 <StandardDirectory Id="ProgramFilesFolder">
10 <Directory Id="INSTALLFOLDER" Name="MergeModule" />
11 </StandardDirectory>
12 </Fragment>
13</Wix>
diff --git a/src/ext/Util/test/WixToolsetTest.Util/TestData/RemoveRegistryKeyEx/ModuleComponents.wxs b/src/ext/Util/test/WixToolsetTest.Util/TestData/RemoveRegistryKeyEx/ModuleComponents.wxs
new file mode 100644
index 00000000..0a0c8cb6
--- /dev/null
+++ b/src/ext/Util/test/WixToolsetTest.Util/TestData/RemoveRegistryKeyEx/ModuleComponents.wxs
@@ -0,0 +1,10 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:util="http://wixtoolset.org/schemas/v4/wxs/util">
2 <Fragment>
3 <ComponentGroup Id="ModuleComponents" Directory="INSTALLFOLDER">
4 <Component>
5 <File Source="ModuleComponents.wxs" />
6 <util:RemoveRegistryKey Root="HKLM" Key="SOFTWARE\Example" On="install" />
7 </Component>
8 </ComponentGroup>
9 </Fragment>
10</Wix>
diff --git a/src/ext/Util/test/WixToolsetTest.Util/TestData/UsingFileShare/Package.en-us.wxl b/src/ext/Util/test/WixToolsetTest.Util/TestData/UsingFileShare/Package.en-us.wxl
new file mode 100644
index 00000000..5301bb1a
--- /dev/null
+++ b/src/ext/Util/test/WixToolsetTest.Util/TestData/UsingFileShare/Package.en-us.wxl
@@ -0,0 +1,9 @@
1<!--
2This file contains the declaration of all the localizable strings.
3-->
4<WixLocalization xmlns="http://wixtoolset.org/schemas/v4/wxl" Culture="en-US">
5
6 <String Id="DowngradeError">A newer version of [ProductName] is already installed.</String>
7 <String Id="FeatureTitle">MsiPackage</String>
8
9</WixLocalization>
diff --git a/src/ext/Util/test/WixToolsetTest.Util/TestData/UsingFileShare/Package.wxs b/src/ext/Util/test/WixToolsetTest.Util/TestData/UsingFileShare/Package.wxs
new file mode 100644
index 00000000..daae573a
--- /dev/null
+++ b/src/ext/Util/test/WixToolsetTest.Util/TestData/UsingFileShare/Package.wxs
@@ -0,0 +1,15 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 <Package Name="MsiPackage" Language="1033" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="047730a5-30fe-4a62-a520-da9381b8226a">
3 <MajorUpgrade DowngradeErrorMessage="!(loc.DowngradeError)" />
4
5 <Feature Id="ProductFeature" Title="!(loc.FeatureTitle)">
6 <ComponentGroupRef Id="ProductComponents" />
7 </Feature>
8 </Package>
9
10 <Fragment>
11 <StandardDirectory Id="ProgramFilesFolder">
12 <Directory Id="INSTALLFOLDER" Name="MsiPackage" />
13 </StandardDirectory>
14 </Fragment>
15</Wix>
diff --git a/src/ext/Util/test/WixToolsetTest.Util/TestData/UsingFileShare/PackageComponents.wxs b/src/ext/Util/test/WixToolsetTest.Util/TestData/UsingFileShare/PackageComponents.wxs
new file mode 100644
index 00000000..7cedbb30
--- /dev/null
+++ b/src/ext/Util/test/WixToolsetTest.Util/TestData/UsingFileShare/PackageComponents.wxs
@@ -0,0 +1,14 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:util="http://wixtoolset.org/schemas/v4/wxs/util">
2 <Fragment>
3 <util:User Id="Everyone" Name="Everyone" />
4
5 <ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
6 <Component>
7 <File Source="example.txt" />
8 <util:FileShare Id="ExampleFileShare" Description="An example file share" Name="example">
9 <util:FileSharePermission User="Everyone" Read="yes" />
10 </util:FileShare>
11 </Component>
12 </ComponentGroup>
13 </Fragment>
14</Wix>
diff --git a/src/ext/Util/test/WixToolsetTest.Util/TestData/UsingFileShare/example.txt b/src/ext/Util/test/WixToolsetTest.Util/TestData/UsingFileShare/example.txt
new file mode 100644
index 00000000..1b4ffe8a
--- /dev/null
+++ b/src/ext/Util/test/WixToolsetTest.Util/TestData/UsingFileShare/example.txt
@@ -0,0 +1 @@
This is example.txt. \ No newline at end of file
diff --git a/src/ext/Util/test/WixToolsetTest.Util/TestData/XmlConfig/Package.en-us.wxl b/src/ext/Util/test/WixToolsetTest.Util/TestData/XmlConfig/Package.en-us.wxl
new file mode 100644
index 00000000..5301bb1a
--- /dev/null
+++ b/src/ext/Util/test/WixToolsetTest.Util/TestData/XmlConfig/Package.en-us.wxl
@@ -0,0 +1,9 @@
1<!--
2This file contains the declaration of all the localizable strings.
3-->
4<WixLocalization xmlns="http://wixtoolset.org/schemas/v4/wxl" Culture="en-US">
5
6 <String Id="DowngradeError">A newer version of [ProductName] is already installed.</String>
7 <String Id="FeatureTitle">MsiPackage</String>
8
9</WixLocalization>
diff --git a/src/ext/Util/test/WixToolsetTest.Util/TestData/XmlConfig/Package.wxs b/src/ext/Util/test/WixToolsetTest.Util/TestData/XmlConfig/Package.wxs
new file mode 100644
index 00000000..a2002634
--- /dev/null
+++ b/src/ext/Util/test/WixToolsetTest.Util/TestData/XmlConfig/Package.wxs
@@ -0,0 +1,17 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:util="http://wixtoolset.org/schemas/v4/wxs/util">
2 <Package Name="MsiPackage" Language="1033" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="047730a5-30fe-4a62-a520-da9381b8226a">
3 <MajorUpgrade DowngradeErrorMessage="!(loc.DowngradeError)" />
4
5 <Feature Id="ProductFeature" Title="!(loc.FeatureTitle)">
6 <Component Id="Del" Directory="INSTALLFOLDER" Guid="3613414c-11f5-40fa-a1f1-a0ba722a6895">
7 <util:XmlConfig Id="DelElement" File="[INSTALLFOLDER]my.xml" Action="delete" Node="element" VerifyPath="xxx" ElementPath="//root/sub" On="install" Sequence="1" />
8 </Component>
9 </Feature>
10 </Package>
11
12 <Fragment>
13 <StandardDirectory Id="ProgramFilesFolder">
14 <Directory Id="INSTALLFOLDER" Name="MsiPackage" />
15 </StandardDirectory>
16 </Fragment>
17</Wix>
diff --git a/src/ext/Util/test/WixToolsetTest.Util/TestData/XmlConfigModule/Module.wxs b/src/ext/Util/test/WixToolsetTest.Util/TestData/XmlConfigModule/Module.wxs
new file mode 100644
index 00000000..29e8555b
--- /dev/null
+++ b/src/ext/Util/test/WixToolsetTest.Util/TestData/XmlConfigModule/Module.wxs
@@ -0,0 +1,19 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:util="http://wixtoolset.org/schemas/v4/wxs/util">
2 <Module Id="XmlConfigModule" Language="1033" Version="1.0.0.0" Guid="047730a5-30fe-4a62-a520-da9381b8226a">
3
4 <Component Id="Parent" Directory="INSTALLFOLDER">
5 <File Id="my.xml" Source="my.xml" />
6 <util:XmlConfig Id="AddElement" File="[my.xml]" Action="create" Node="element" VerifyPath="xxx" ElementPath="//root/sub" On="install" Sequence="1" />
7 </Component>
8
9 <Component Id="Child" Directory="INSTALLFOLDER" Guid="4613414c-11f5-40fa-a1f1-a0ba722a6895">
10 <util:XmlConfig Id="ChildElement" File="[my.xml]" VerifyPath="xxx" ElementId="AddElement" Sequence="1" />
11 </Component>
12 </Module>
13
14 <Fragment>
15 <StandardDirectory Id="ProgramFilesFolder">
16 <Directory Id="INSTALLFOLDER" Name="MsiPackage" />
17 </StandardDirectory>
18 </Fragment>
19</Wix>
diff --git a/src/ext/Util/test/WixToolsetTest.Util/TestData/XmlConfigModule/my.xml b/src/ext/Util/test/WixToolsetTest.Util/TestData/XmlConfigModule/my.xml
new file mode 100644
index 00000000..bad25217
--- /dev/null
+++ b/src/ext/Util/test/WixToolsetTest.Util/TestData/XmlConfigModule/my.xml
@@ -0,0 +1 @@
This is my.xml file.
diff --git a/src/ext/Util/test/WixToolsetTest.Util/UtilExtensionFixture.cs b/src/ext/Util/test/WixToolsetTest.Util/UtilExtensionFixture.cs
new file mode 100644
index 00000000..883f9794
--- /dev/null
+++ b/src/ext/Util/test/WixToolsetTest.Util/UtilExtensionFixture.cs
@@ -0,0 +1,317 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolsetTest.Util
4{
5 using System.IO;
6 using System.Linq;
7 using WixBuildTools.TestSupport;
8 using WixToolset.Core.TestPackage;
9 using WixToolset.Data;
10 using WixToolset.Data.Symbols;
11 using WixToolset.Util;
12 using Xunit;
13
14 public class UtilExtensionFixture
15 {
16 [Fact]
17 public void CanBuildUsingFileShare()
18 {
19 var folder = TestData.Get(@"TestData\UsingFileShare");
20 var build = new Builder(folder, typeof(UtilExtensionFactory), new[] { folder });
21
22 var results = build.BuildAndQuery(Build, "Binary", "CustomAction", "Wix4FileShare", "Wix4FileSharePermissions");
23 WixAssert.CompareLineByLine(new[]
24 {
25 "Binary:Wix4UtilCA_X86\t[Binary data]",
26 "CustomAction:Wix4ConfigureSmbInstall_X86\t1\tWix4UtilCA_X86\tConfigureSmbInstall\t",
27 "CustomAction:Wix4ConfigureSmbUninstall_X86\t1\tWix4UtilCA_X86\tConfigureSmbUninstall\t",
28 "CustomAction:Wix4CreateSmb_X86\t11265\tWix4UtilCA_X86\tCreateSmb\t",
29 "CustomAction:Wix4CreateSmbRollback_X86\t11585\tWix4UtilCA_X86\tDropSmb\t",
30 "CustomAction:Wix4DropSmb_X86\t11265\tWix4UtilCA_X86\tDropSmb\t",
31 "CustomAction:Wix4DropSmbRollback_X86\t11585\tWix4UtilCA_X86\tCreateSmb\t",
32 "Wix4FileShare:ExampleFileShare\texample\tfilF5_pLhBuF5b4N9XEo52g_hUM5Lo\tAn example file share\tINSTALLFOLDER",
33 "Wix4FileSharePermissions:ExampleFileShare\tEveryone\t1",
34 }, results.OrderBy(s => s).ToArray());
35 }
36
37 [Fact]
38 public void CanBuildUsingFileShareX64()
39 {
40 var folder = TestData.Get(@"TestData\UsingFileShare");
41 var build = new Builder(folder, typeof(UtilExtensionFactory), new[] { folder });
42
43 var results = build.BuildAndQuery(BuildX64, "Binary", "CustomAction", "Wix4FileShare", "Wix4FileSharePermissions");
44 WixAssert.CompareLineByLine(new[]
45 {
46 "Binary:Wix4UtilCA_X64\t[Binary data]",
47 "CustomAction:Wix4ConfigureSmbInstall_X64\t1\tWix4UtilCA_X64\tConfigureSmbInstall\t",
48 "CustomAction:Wix4ConfigureSmbUninstall_X64\t1\tWix4UtilCA_X64\tConfigureSmbUninstall\t",
49 "CustomAction:Wix4CreateSmb_X64\t11265\tWix4UtilCA_X64\tCreateSmb\t",
50 "CustomAction:Wix4CreateSmbRollback_X64\t11585\tWix4UtilCA_X64\tDropSmb\t",
51 "CustomAction:Wix4DropSmb_X64\t11265\tWix4UtilCA_X64\tDropSmb\t",
52 "CustomAction:Wix4DropSmbRollback_X64\t11585\tWix4UtilCA_X64\tCreateSmb\t",
53 "Wix4FileShare:ExampleFileShare\texample\tfilF5_pLhBuF5b4N9XEo52g_hUM5Lo\tAn example file share\tINSTALLFOLDER",
54 "Wix4FileSharePermissions:ExampleFileShare\tEveryone\t1",
55 }, results.OrderBy(s => s).ToArray());
56 }
57
58 [Fact]
59 public void CanBuildCloseApplication()
60 {
61 var folder = TestData.Get(@"TestData\CloseApplication");
62 var build = new Builder(folder, typeof(UtilExtensionFactory), new[] { folder });
63
64 var results = build.BuildAndQuery(BuildARM64, "Binary", "CustomAction", "Wix4CloseApplication");
65 WixAssert.CompareLineByLine(new[]
66 {
67 "Binary:Wix4UtilCA_A64\t[Binary data]",
68 "CustomAction:Wix4CheckRebootRequired_A64\t65\tWix4UtilCA_A64\tWixCheckRebootRequired\t",
69 "CustomAction:Wix4CloseApplications_A64\t1\tWix4UtilCA_A64\tWixCloseApplications\t",
70 "CustomAction:Wix4CloseApplicationsDeferred_A64\t3073\tWix4UtilCA_A64\tWixCloseApplicationsDeferred\t",
71 "Wix4CloseApplication:CloseMyApp\texplorer.exe\t\t\t3\t\tMYAPPISRUNNING\t\t",
72 }, results.OrderBy(s => s).ToArray());
73 }
74
75 [Fact]
76 public void CanBuildInternetShortcutInProduct()
77 {
78 var folder = TestData.Get(@"TestData\InternetShortcut");
79 var build = new Builder(folder, typeof(UtilExtensionFactory), new[] { folder });
80
81 var results = build.BuildAndQuery(BuildX64, "Binary", "CustomAction", "RemoveFile", "Wix4InternetShortcut");
82 WixAssert.CompareLineByLine(new[]
83 {
84 "Binary:Wix4UtilCA_X64\t[Binary data]",
85 "CustomAction:Wix4CreateInternetShortcuts_X64\t3073\tWix4UtilCA_X64\tWixCreateInternetShortcuts\t",
86 "CustomAction:Wix4RollbackInternetShortcuts_X64\t3329\tWix4UtilCA_X64\tWixRollbackInternetShortcuts\t",
87 "CustomAction:Wix4SchedInternetShortcuts_X64\t1\tWix4UtilCA_X64\tWixSchedInternetShortcuts\t",
88 "RemoveFile:uisdCsU32.1i4Hebrg1N7E194zJQ8Y\tPackage.ico\thoiptxrr.url|WiX Toolset (url).url\tINSTALLFOLDER\t2",
89 "RemoveFile:uisjV.q0ROZZYR3h_lkpbkZtLtPH0A\tPackage.ico\tjcxd1dwf.lnk|WiX Toolset (link).lnk\tINSTALLFOLDER\t2",
90 "Wix4InternetShortcut:uisdCsU32.1i4Hebrg1N7E194zJQ8Y\tPackage.ico\tINSTALLFOLDER\tWiX Toolset (url).url\thttps://wixtoolset.org\t1\t[#Package.ico]\t0",
91 "Wix4InternetShortcut:uisjV.q0ROZZYR3h_lkpbkZtLtPH0A\tPackage.ico\tINSTALLFOLDER\tWiX Toolset (link).lnk\thttps://wixtoolset.org\t0\t[#Package.ico]\t0",
92 }, results.OrderBy(s => s).ToArray());
93 }
94
95 [Fact]
96 public void CanBuildInternetShortcutInMergeModule()
97 {
98 var folder = TestData.Get(@"TestData\InternetShortcutModule");
99 var build = new Builder(folder, typeof(UtilExtensionFactory), new[] { folder }, "test.msm");
100
101 var results = build.BuildAndQuery(BuildX64, "Binary", "CustomAction", "RemoveFile", "Wix4InternetShortcut");
102 WixAssert.CompareLineByLine(new[]
103 {
104 "Binary:Wix4UtilCA_X64.047730A5_30FE_4A62_A520_DA9381B8226A\t[Binary data]",
105 "CustomAction:Wix4CreateInternetShortcuts_X64\t3073\tWix4UtilCA_X64.047730A5_30FE_4A62_A520_DA9381B8226A\tWixCreateInternetShortcuts\t",
106 "CustomAction:Wix4RollbackInternetShortcuts_X64\t3329\tWix4UtilCA_X64.047730A5_30FE_4A62_A520_DA9381B8226A\tWixRollbackInternetShortcuts\t",
107 "CustomAction:Wix4SchedInternetShortcuts_X64\t1\tWix4UtilCA_X64.047730A5_30FE_4A62_A520_DA9381B8226A\tWixSchedInternetShortcuts\t",
108 "RemoveFile:uisdCsU32.1i4Hebrg1N7E194zJQ8Y.047730A5_30FE_4A62_A520_DA9381B8226A\tPackage.ico.047730A5_30FE_4A62_A520_DA9381B8226A\thoiptxrr.url|WiX Toolset (url).url\tINSTALLFOLDER.047730A5_30FE_4A62_A520_DA9381B8226A\t2",
109 "RemoveFile:uisjV.q0ROZZYR3h_lkpbkZtLtPH0A.047730A5_30FE_4A62_A520_DA9381B8226A\tPackage.ico.047730A5_30FE_4A62_A520_DA9381B8226A\tjcxd1dwf.lnk|WiX Toolset (link).lnk\tINSTALLFOLDER.047730A5_30FE_4A62_A520_DA9381B8226A\t2",
110 "Wix4InternetShortcut:uisdCsU32.1i4Hebrg1N7E194zJQ8Y.047730A5_30FE_4A62_A520_DA9381B8226A\tPackage.ico.047730A5_30FE_4A62_A520_DA9381B8226A\tINSTALLFOLDER.047730A5_30FE_4A62_A520_DA9381B8226A\tWiX Toolset (url).url\thttps://wixtoolset.org\t1\t[#Package.ico.047730A5_30FE_4A62_A520_DA9381B8226A]\t0",
111 "Wix4InternetShortcut:uisjV.q0ROZZYR3h_lkpbkZtLtPH0A.047730A5_30FE_4A62_A520_DA9381B8226A\tPackage.ico.047730A5_30FE_4A62_A520_DA9381B8226A\tINSTALLFOLDER.047730A5_30FE_4A62_A520_DA9381B8226A\tWiX Toolset (link).lnk\thttps://wixtoolset.org\t0\t[#Package.ico.047730A5_30FE_4A62_A520_DA9381B8226A]\t0",
112 }, results.OrderBy(s => s).ToArray());
113 }
114
115 [Fact]
116 public void CanBuildWithPermissionEx()
117 {
118 var folder = TestData.Get(@"TestData\PermissionEx");
119 var build = new Builder(folder, typeof(UtilExtensionFactory), new[] { folder });
120
121 var results = build.BuildAndQuery(BuildX64, "Wix4SecureObject");
122 WixAssert.CompareLineByLine(new[]
123 {
124 "Wix4SecureObject:ExampleRegistryKey\tRegistry\t\tEveryone\t1\t268435456\tfilF5_pLhBuF5b4N9XEo52g_hUM5Lo",
125 "Wix4SecureObject:filF5_pLhBuF5b4N9XEo52g_hUM5Lo\tFile\t\tEveryone\t1\t268435456\tfilF5_pLhBuF5b4N9XEo52g_hUM5Lo",
126 "Wix4SecureObject:INSTALLFOLDER\tCreateFolder\t\tEveryone\t1\t268435456\tfilF5_pLhBuF5b4N9XEo52g_hUM5Lo",
127 "Wix4SecureObject:regL6DnQ9yJpDJH5OdcVji4YXsdX2c\tRegistry\t\tEveryone\t1\t268435456\tfilF5_pLhBuF5b4N9XEo52g_hUM5Lo",
128 "Wix4SecureObject:testsvc\tServiceInstall\t\tEveryone\t1\t268435456\tfilF5_pLhBuF5b4N9XEo52g_hUM5Lo",
129 }, results.OrderBy(s => s).ToArray());
130 }
131
132 [Fact]
133 public void CanBuildRemoveRegistryKeyExInMergeModule()
134 {
135 var folder = TestData.Get(@"TestData", "RemoveRegistryKeyEx");
136 var build = new Builder(folder, typeof(UtilExtensionFactory), new[] { folder }, "test.msm");
137
138 var results = build.BuildAndQuery(BuildX64, "Binary", "CustomAction", "RemoveRegistry", "Wix4RemoveRegistryKeyEx");
139 WixAssert.CompareLineByLine(new[]
140 {
141 "Binary:Wix4UtilCA_X64.047730A5_30FE_4A62_A520_DA9381B8226A\t[Binary data]",
142 "CustomAction:Wix4RemoveRegistryKeysEx_X64.047730A5_30FE_4A62_A520_DA9381B8226A\t65\tWix4UtilCA_X64.047730A5_30FE_4A62_A520_DA9381B8226A\tWixRemoveRegistryKeysEx\t",
143 "Wix4RemoveRegistryKeyEx:rrxfcDhR4HhE3v3rYiQcNtQjyahQNg.047730A5_30FE_4A62_A520_DA9381B8226A\tfilh4juyUVjoUcWWtcQmd5L07FoON4.047730A5_30FE_4A62_A520_DA9381B8226A\t2\tSOFTWARE\\Example\t1\t",
144 }, results.OrderBy(s => s).ToArray());
145 }
146
147 [Fact]
148 public void CanBuildRemoveFolderExInMergeModule()
149 {
150 var folder = TestData.Get(@"TestData\RemoveFolderEx");
151 var build = new Builder(folder, typeof(UtilExtensionFactory), new[] { folder }, "test.msm");
152
153 var results = build.BuildAndQuery(BuildX64, "Binary", "CustomAction", "RemoveFile", "Wix4RemoveFolderEx");
154 WixAssert.CompareLineByLine(new[]
155 {
156 "Binary:Wix4UtilCA_X64.047730A5_30FE_4A62_A520_DA9381B8226A\t[Binary data]",
157 "CustomAction:Wix4RemoveFoldersEx_X64.047730A5_30FE_4A62_A520_DA9381B8226A\t65\tWix4UtilCA_X64.047730A5_30FE_4A62_A520_DA9381B8226A\tWixRemoveFoldersEx\t",
158 "Wix4RemoveFolderEx:wrf5qCm1SE.zp8djrlk78l1IYFXsEw.047730A5_30FE_4A62_A520_DA9381B8226A\tfilh4juyUVjoUcWWtcQmd5L07FoON4.047730A5_30FE_4A62_A520_DA9381B8226A\tRemoveProp.047730A5_30FE_4A62_A520_DA9381B8226A\t3\t",
159 }, results.OrderBy(s => s).ToArray());
160 }
161
162 [Fact]
163 public void CanBuildWithEventManifest()
164 {
165 var folder = TestData.Get(@"TestData\EventManifest");
166 var build = new Builder(folder, typeof(UtilExtensionFactory), new[] { folder });
167
168 var results = build.BuildAndQuery(BuildARM64, "Binary", "CustomAction", "Wix4EventManifest", "Wix4XmlFile");
169 WixAssert.CompareLineByLine(new[]
170 {
171 "Binary:Wix4UtilCA_A64\t[Binary data]",
172 "CustomAction:Wix4ConfigureEventManifestRegister_A64\t1\tWix4UtilCA_A64\tConfigureEventManifestRegister\t",
173 "CustomAction:Wix4ConfigureEventManifestUnregister_A64\t1\tWix4UtilCA_A64\tConfigureEventManifestUnregister\t",
174 "CustomAction:Wix4ExecXmlFile_A64\t11265\tWix4UtilCA_A64\tExecXmlFile\t",
175 "CustomAction:Wix4ExecXmlFileRollback_A64\t11521\tWix4UtilCA_A64\tExecXmlFileRollback\t",
176 "CustomAction:Wix4RegisterEventManifest_A64\t3073\tWix4UtilCA_A64\tWixQuietExec\t",
177 "CustomAction:Wix4RollbackRegisterEventManifest_A64\t3393\tWix4UtilCA_A64\tWixQuietExec\t",
178 "CustomAction:Wix4RollbackUnregisterEventManifest_A64\t3329\tWix4UtilCA_A64\tWixQuietExec\t",
179 "CustomAction:Wix4SchedXmlFile_A64\t1\tWix4UtilCA_A64\tSchedXmlFile\t",
180 "CustomAction:Wix4UnregisterEventManifest_A64\t3137\tWix4UtilCA_A64\tWixQuietExec\t",
181 "Wix4EventManifest:Manifest.dll\t[#Manifest.dll]",
182 "Wix4XmlFile:Config_Manifest.dllMessageFile\t[#Manifest.dll]\t/*/*/*/*[\\[]@messageFileName[\\]]\tmessageFileName\t[Manifest.dll]\t4100\tManifest.dll\t",
183 "Wix4XmlFile:Config_Manifest.dllResourceFile\t[#Manifest.dll]\t/*/*/*/*[\\[]@resourceFileName[\\]]\tresourceFileName\t[Manifest.dll]\t4100\tManifest.dll\t",
184 }, results.OrderBy(s => s).ToArray());
185 }
186
187 [Fact]
188 public void CanBuildWithQueries()
189 {
190 var folder = TestData.Get(@"TestData\Queries");
191 var build = new Builder(folder, typeof(UtilExtensionFactory), new[] { folder });
192
193 var results = build.BuildAndQuery(BuildARM64, "Binary", "CustomAction");
194 WixAssert.CompareLineByLine(new[]
195 {
196 "Binary:Wix4UtilCA_A64\t[Binary data]",
197 "CustomAction:Wix4BroadcastEnvironmentChange_A64\t65\tWix4UtilCA_A64\tWixBroadcastEnvironmentChange\t",
198 "CustomAction:Wix4BroadcastSettingChange_A64\t65\tWix4UtilCA_A64\tWixBroadcastSettingChange\t",
199 "CustomAction:Wix4CheckRebootRequired_A64\t65\tWix4UtilCA_A64\tWixCheckRebootRequired\t",
200 "CustomAction:Wix4QueryOsDriverInfo_A64\t257\tWix4UtilCA_A64\tWixQueryOsDriverInfo\t",
201 "CustomAction:Wix4QueryOsInfo_A64\t257\tWix4UtilCA_A64\tWixQueryOsInfo\t",
202 }, results.OrderBy(s => s).ToArray());
203 }
204
205 [Fact]
206 public void CanBuildWithXmlConfig()
207 {
208 var folder = TestData.Get(@"TestData", "XmlConfig");
209 var build = new Builder(folder, typeof(UtilExtensionFactory), new[] { folder });
210
211 var results = build.BuildAndQuery(BuildX64, "Wix4XmlConfig");
212 WixAssert.CompareLineByLine(new[]
213 {
214 "Wix4XmlConfig:DelElement\t[INSTALLFOLDER]my.xml\t\t//root/sub\txxx\t\t\t289\tDel\t1",
215 }, results.OrderBy(s => s).ToArray());
216 }
217
218 [Fact]
219 public void CanBuildModuleWithXmlConfig()
220 {
221 var folder = TestData.Get(@"TestData", "XmlConfigModule");
222 var build = new Builder(folder, typeof(UtilExtensionFactory), new[] { folder });
223
224 var results = build.BuildAndQuery(BuildX64, "Wix4XmlConfig");
225 WixAssert.CompareLineByLine(new[]
226 {
227 "Wix4XmlConfig:AddElement.047730A5_30FE_4A62_A520_DA9381B8226A\t[my.xml.047730A5_30FE_4A62_A520_DA9381B8226A]\t\t//root/sub\txxx\t\t\t273\tParent.047730A5_30FE_4A62_A520_DA9381B8226A\t1",
228 "Wix4XmlConfig:ChildElement.047730A5_30FE_4A62_A520_DA9381B8226A\t[my.xml.047730A5_30FE_4A62_A520_DA9381B8226A]\tAddElement.047730A5_30FE_4A62_A520_DA9381B8226A\t\txxx\t\t\t0\tChild.047730A5_30FE_4A62_A520_DA9381B8226A\t1",
229 }, results.OrderBy(s => s).ToArray());
230 }
231
232 [Fact]
233 public void CanBuildBundleWithSearches()
234 {
235 var burnStubPath = TestData.Get(@"TestData\.Data\burn.exe");
236 var folder = TestData.Get(@"TestData\BundleWithSearches");
237 var rootFolder = TestData.Get();
238 var wixext = Path.Combine(rootFolder, "WixToolset.Util.wixext.dll");
239
240 using (var fs = new DisposableFileSystem())
241 {
242 var baseFolder = fs.GetFolder();
243 var intermediateFolder = Path.Combine(baseFolder, "obj");
244 var bundlePath = Path.Combine(baseFolder, @"bin\test.exe");
245 var baFolderPath = Path.Combine(baseFolder, "ba");
246 var extractFolderPath = Path.Combine(baseFolder, "extract");
247
248 var result = WixRunner.Execute(new[]
249 {
250 "build",
251 Path.Combine(folder, "Bundle.wxs"),
252 "-ext", wixext,
253 "-loc", Path.Combine(folder, "Bundle.en-us.wxl"),
254 "-bindpath", Path.Combine(folder, "data"),
255 "-intermediateFolder", intermediateFolder,
256 "-o", bundlePath
257 });
258
259 result.AssertSuccess();
260
261 Assert.True(File.Exists(bundlePath));
262#if TODO
263 Assert.True(File.Exists(Path.Combine(baseFolder, @"bin\test.wixpdb")));
264#endif
265
266 var extractResult = BundleExtractor.ExtractBAContainer(null, bundlePath, baFolderPath, extractFolderPath);
267 extractResult.AssertSuccess();
268
269 var bundleExtensionDatas = extractResult.SelectBundleExtensionDataNodes("/be:BundleExtensionData/be:BundleExtension[@Id='Wix4UtilBundleExtension_X86']");
270 Assert.Equal(1, bundleExtensionDatas.Count);
271 Assert.Equal("<BundleExtension Id='Wix4UtilBundleExtension_X86'>" +
272 "<WixWindowsFeatureSearch Id='DetectSHA2SupportId' Type='sha2CodeSigning' />" +
273 "</BundleExtension>", bundleExtensionDatas[0].GetTestXml());
274
275 var utilSearches = extractResult.SelectManifestNodes("/burn:BurnManifest/*[self::burn:ExtensionSearch or self::burn:FileSearch or self::burn:MsiProductSearch or self::burn:RegistrySearch]");
276 Assert.Equal(5, utilSearches.Count);
277 Assert.Equal("<ExtensionSearch Id='DetectSHA2SupportId' Variable='IsSHA2Supported' " +
278 "ExtensionId='Wix4UtilBundleExtension_X86' />", utilSearches[0].GetTestXml());
279 Assert.Equal("<FileSearch Id='FileSearchId' Variable='FileSearchVariable' " +
280 $@"Path='%windir%\System32\mscoree.dll' Type='exists' />", utilSearches[1].GetTestXml());
281 Assert.Equal("<MsiProductSearch Id='ProductSearchId' Variable='ProductSearchVariable' Condition='1 &amp; 2 &lt; 3' " +
282 "UpgradeCode='{738D02BF-E231-4370-8209-E9FD4E1BE2A1}' Type='version' />", utilSearches[2].GetTestXml());
283 Assert.Equal("<RegistrySearch Id='RegistrySearchId' Variable='RegistrySearchVariable' " +
284 @"Root='HKLM' Key='SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full' Value='Release' Type='value' VariableType='string' />", utilSearches[3].GetTestXml());
285 Assert.Equal("<RegistrySearch Id='RegistrySearchId64' Variable='RegistrySearchVariable64' " +
286 @"Root='HKLM' Key='SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full' Value='Release' Type='value' Win64='yes' VariableType='string' />", utilSearches[4].GetTestXml());
287 }
288 }
289
290 private static void Build(string[] args)
291 {
292 var result = WixRunner.Execute(args);
293 result.AssertSuccess();
294 }
295
296 private static void BuildX64(string[] args)
297 {
298 var newArgs = args.ToList();
299 newArgs.Add("-platform");
300 newArgs.Add("x64");
301 newArgs.Add("-sw1072");
302
303 var result = WixRunner.Execute(newArgs.ToArray());
304 result.AssertSuccess();
305 }
306
307 private static void BuildARM64(string[] args)
308 {
309 var newArgs = args.ToList();
310 newArgs.Add("-platform");
311 newArgs.Add("arm64");
312
313 var result = WixRunner.Execute(newArgs.ToArray());
314 result.AssertSuccess();
315 }
316 }
317}
diff --git a/src/ext/Util/test/WixToolsetTest.Util/WixToolsetTest.Util.csproj b/src/ext/Util/test/WixToolsetTest.Util/WixToolsetTest.Util.csproj
new file mode 100644
index 00000000..e77ecbed
--- /dev/null
+++ b/src/ext/Util/test/WixToolsetTest.Util/WixToolsetTest.Util.csproj
@@ -0,0 +1,38 @@
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 Sdk="Microsoft.NET.Sdk">
5 <PropertyGroup>
6 <TargetFramework>netcoreapp3.1</TargetFramework>
7 <IsPackable>false</IsPackable>
8 </PropertyGroup>
9
10 <PropertyGroup>
11 <NoWarn>NU1701</NoWarn>
12 </PropertyGroup>
13
14 <ItemGroup>
15 <Content Include="TestData\**" CopyToOutputDirectory="PreserveNewest" />
16 </ItemGroup>
17
18 <ItemGroup>
19 <ProjectReference Include="..\..\wixext\WixToolset.Util.wixext.csproj" />
20 </ItemGroup>
21
22 <ItemGroup>
23 <PackageReference Include="WixToolset.Core" Version="4.0.*" />
24 <PackageReference Include="WixToolset.Core.Burn" Version="4.0.*" />
25 <PackageReference Include="WixToolset.Core.WindowsInstaller" Version="4.0.*" />
26 <PackageReference Include="WixToolset.Core.TestPackage" Version="4.0.*" />
27 </ItemGroup>
28
29 <ItemGroup>
30 <PackageReference Include="WixBuildTools.TestSupport" Version="4.0.*" />
31 </ItemGroup>
32
33 <ItemGroup>
34 <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.3.0" />
35 <PackageReference Include="xunit" Version="2.4.1" />
36 <PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" PrivateAssets="All" />
37 </ItemGroup>
38</Project>
diff --git a/src/ext/Util/test/WixToolsetTest.Util/WixToolsetTest.Util.v3.ncrunchproject b/src/ext/Util/test/WixToolsetTest.Util/WixToolsetTest.Util.v3.ncrunchproject
new file mode 100644
index 00000000..7b5b2139
--- /dev/null
+++ b/src/ext/Util/test/WixToolsetTest.Util/WixToolsetTest.Util.v3.ncrunchproject
@@ -0,0 +1,5 @@
1<ProjectConfiguration>
2 <Settings>
3 <CopyReferencedAssembliesToWorkspace>True</CopyReferencedAssembliesToWorkspace>
4 </Settings>
5</ProjectConfiguration> \ No newline at end of file
diff --git a/src/ext/Util/wix.snk b/src/ext/Util/wix.snk
new file mode 100644
index 00000000..3908a66a
--- /dev/null
+++ b/src/ext/Util/wix.snk
Binary files differ
diff --git a/src/ext/Util/wixext/PerformanceCounterType.cs b/src/ext/Util/wixext/PerformanceCounterType.cs
new file mode 100644
index 00000000..1e06efd3
--- /dev/null
+++ b/src/ext/Util/wixext/PerformanceCounterType.cs
@@ -0,0 +1,192 @@
1// Captured from: C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6.1\System.dll
2
3namespace System.Diagnostics
4{
5 public enum PerformanceCounterType
6 {
7 //
8 // Summary:
9 // An instantaneous counter that shows the most recently observed value in hexadecimal
10 // format. Used, for example, to maintain a simple count of items or operations.
11 NumberOfItemsHEX32 = 0,
12 //
13 // Summary:
14 // An instantaneous counter that shows the most recently observed value. Used, for
15 // example, to maintain a simple count of a very large number of items or operations.
16 // It is the same as NumberOfItemsHEX32 except that it uses larger fields to accommodate
17 // larger values.
18 NumberOfItemsHEX64 = 256,
19 //
20 // Summary:
21 // An instantaneous counter that shows the most recently observed value. Used, for
22 // example, to maintain a simple count of items or operations.
23 NumberOfItems32 = 65536,
24 //
25 // Summary:
26 // An instantaneous counter that shows the most recently observed value. Used, for
27 // example, to maintain a simple count of a very large number of items or operations.
28 // It is the same as NumberOfItems32 except that it uses larger fields to accommodate
29 // larger values.
30 NumberOfItems64 = 65792,
31 //
32 // Summary:
33 // A difference counter that shows the change in the measured attribute between
34 // the two most recent sample intervals.
35 CounterDelta32 = 4195328,
36 //
37 // Summary:
38 // A difference counter that shows the change in the measured attribute between
39 // the two most recent sample intervals. It is the same as the CounterDelta32 counter
40 // type except that is uses larger fields to accomodate larger values.
41 CounterDelta64 = 4195584,
42 //
43 // Summary:
44 // An average counter that shows the average number of operations completed in one
45 // second. When a counter of this type samples the data, each sampling interrupt
46 // returns one or zero. The counter data is the number of ones that were sampled.
47 // It measures time in units of ticks of the system performance timer.
48 SampleCounter = 4260864,
49 //
50 // Summary:
51 // An average counter designed to monitor the average length of a queue to a resource
52 // over time. It shows the difference between the queue lengths observed during
53 // the last two sample intervals divided by the duration of the interval. This type
54 // of counter is typically used to track the number of items that are queued or
55 // waiting.
56 CountPerTimeInterval32 = 4523008,
57 //
58 // Summary:
59 // An average counter that monitors the average length of a queue to a resource
60 // over time. Counters of this type display the difference between the queue lengths
61 // observed during the last two sample intervals, divided by the duration of the
62 // interval. This counter type is the same as CountPerTimeInterval32 except that
63 // it uses larger fields to accommodate larger values. This type of counter is typically
64 // used to track a high-volume or very large number of items that are queued or
65 // waiting.
66 CountPerTimeInterval64 = 4523264,
67 //
68 // Summary:
69 // A difference counter that shows the average number of operations completed during
70 // each second of the sample interval. Counters of this type measure time in ticks
71 // of the system clock.
72 RateOfCountsPerSecond32 = 272696320,
73 //
74 // Summary:
75 // A difference counter that shows the average number of operations completed during
76 // each second of the sample interval. Counters of this type measure time in ticks
77 // of the system clock. This counter type is the same as the RateOfCountsPerSecond32
78 // type, but it uses larger fields to accommodate larger values to track a high-volume
79 // number of items or operations per second, such as a byte-transmission rate.
80 RateOfCountsPerSecond64 = 272696576,
81 //
82 // Summary:
83 // An instantaneous percentage counter that shows the ratio of a subset to its set
84 // as a percentage. For example, it compares the number of bytes in use on a disk
85 // to the total number of bytes on the disk. Counters of this type display the current
86 // percentage only, not an average over time.
87 RawFraction = 537003008,
88 //
89 // Summary:
90 // A percentage counter that shows the average time that a component is active as
91 // a percentage of the total sample time.
92 CounterTimer = 541132032,
93 //
94 // Summary:
95 // A percentage counter that shows the active time of a component as a percentage
96 // of the total elapsed time of the sample interval. It measures time in units of
97 // 100 nanoseconds (ns). Counters of this type are designed to measure the activity
98 // of one component at a time.
99 Timer100Ns = 542180608,
100 //
101 // Summary:
102 // A percentage counter that shows the average ratio of hits to all operations during
103 // the last two sample intervals.
104 SampleFraction = 549585920,
105 //
106 // Summary:
107 // A percentage counter that displays the average percentage of active time observed
108 // during sample interval. The value of these counters is calculated by monitoring
109 // the percentage of time that the service was inactive and then subtracting that
110 // value from 100 percent.
111 CounterTimerInverse = 557909248,
112 //
113 // Summary:
114 // A percentage counter that shows the average percentage of active time observed
115 // during the sample interval.
116 Timer100NsInverse = 558957824,
117 //
118 // Summary:
119 // A percentage counter that displays the active time of one or more components
120 // as a percentage of the total time of the sample interval. Because the numerator
121 // records the active time of components operating simultaneously, the resulting
122 // percentage can exceed 100 percent.
123 CounterMultiTimer = 574686464,
124 //
125 // Summary:
126 // A percentage counter that shows the active time of one or more components as
127 // a percentage of the total time of the sample interval. It measures time in 100
128 // nanosecond (ns) units.
129 CounterMultiTimer100Ns = 575735040,
130 //
131 // Summary:
132 // A percentage counter that shows the active time of one or more components as
133 // a percentage of the total time of the sample interval. It derives the active
134 // time by measuring the time that the components were not active and subtracting
135 // the result from 100 percent by the number of objects monitored.
136 CounterMultiTimerInverse = 591463680,
137 //
138 // Summary:
139 // A percentage counter that shows the active time of one or more components as
140 // a percentage of the total time of the sample interval. Counters of this type
141 // measure time in 100 nanosecond (ns) units. They derive the active time by measuring
142 // the time that the components were not active and subtracting the result from
143 // multiplying 100 percent by the number of objects monitored.
144 CounterMultiTimer100NsInverse = 592512256,
145 //
146 // Summary:
147 // An average counter that measures the time it takes, on average, to complete a
148 // process or operation. Counters of this type display a ratio of the total elapsed
149 // time of the sample interval to the number of processes or operations completed
150 // during that time. This counter type measures time in ticks of the system clock.
151 AverageTimer32 = 805438464,
152 //
153 // Summary:
154 // A difference timer that shows the total time between when the component or process
155 // started and the time when this value is calculated.
156 ElapsedTime = 807666944,
157 //
158 // Summary:
159 // An average counter that shows how many items are processed, on average, during
160 // an operation. Counters of this type display a ratio of the items processed to
161 // the number of operations completed. The ratio is calculated by comparing the
162 // number of items processed during the last interval to the number of operations
163 // completed during the last interval.
164 AverageCount64 = 1073874176,
165 //
166 // Summary:
167 // A base counter that stores the number of sampling interrupts taken and is used
168 // as a denominator in the sampling fraction. The sampling fraction is the number
169 // of samples that were 1 (or true) for a sample interrupt. Check that this value
170 // is greater than zero before using it as the denominator in a calculation of SampleFraction.
171 SampleBase = 1073939457,
172 //
173 // Summary:
174 // A base counter that is used in the calculation of time or count averages, such
175 // as AverageTimer32 and AverageCount64. Stores the denominator for calculating
176 // a counter to present "time per operation" or "count per operation".
177 AverageBase = 1073939458,
178 //
179 // Summary:
180 // A base counter that stores the denominator of a counter that presents a general
181 // arithmetic fraction. Check that this value is greater than zero before using
182 // it as the denominator in a RawFraction value calculation.
183 RawBase = 1073939459,
184 //
185 // Summary:
186 // A base counter that indicates the number of items sampled. It is used as the
187 // denominator in the calculations to get an average among the items sampled when
188 // taking timings of multiple, but similar items. Used with CounterMultiTimer, CounterMultiTimerInverse,
189 // CounterMultiTimer100Ns, and CounterMultiTimer100NsInverse.
190 CounterMultiBase = 1107494144
191 }
192}
diff --git a/src/ext/Util/wixext/Symbols/EventManifestSymbol.cs b/src/ext/Util/wixext/Symbols/EventManifestSymbol.cs
new file mode 100644
index 00000000..ccd3c899
--- /dev/null
+++ b/src/ext/Util/wixext/Symbols/EventManifestSymbol.cs
@@ -0,0 +1,55 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Util
4{
5 using WixToolset.Data;
6 using WixToolset.Util.Symbols;
7
8 public static partial class UtilSymbolDefinitions
9 {
10 public static readonly IntermediateSymbolDefinition EventManifest = new IntermediateSymbolDefinition(
11 UtilSymbolDefinitionType.EventManifest.ToString(),
12 new[]
13 {
14 new IntermediateFieldDefinition(nameof(EventManifestSymbolFields.ComponentRef), IntermediateFieldType.String),
15 new IntermediateFieldDefinition(nameof(EventManifestSymbolFields.File), IntermediateFieldType.String),
16 },
17 typeof(EventManifestSymbol));
18 }
19}
20
21namespace WixToolset.Util.Symbols
22{
23 using WixToolset.Data;
24
25 public enum EventManifestSymbolFields
26 {
27 ComponentRef,
28 File,
29 }
30
31 public class EventManifestSymbol : IntermediateSymbol
32 {
33 public EventManifestSymbol() : base(UtilSymbolDefinitions.EventManifest, null, null)
34 {
35 }
36
37 public EventManifestSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(UtilSymbolDefinitions.EventManifest, sourceLineNumber, id)
38 {
39 }
40
41 public IntermediateField this[EventManifestSymbolFields index] => this.Fields[(int)index];
42
43 public string ComponentRef
44 {
45 get => this.Fields[(int)EventManifestSymbolFields.ComponentRef].AsString();
46 set => this.Set((int)EventManifestSymbolFields.ComponentRef, value);
47 }
48
49 public string File
50 {
51 get => this.Fields[(int)EventManifestSymbolFields.File].AsString();
52 set => this.Set((int)EventManifestSymbolFields.File, value);
53 }
54 }
55} \ No newline at end of file
diff --git a/src/ext/Util/wixext/Symbols/FileSharePermissionsSymbol.cs b/src/ext/Util/wixext/Symbols/FileSharePermissionsSymbol.cs
new file mode 100644
index 00000000..3db92f22
--- /dev/null
+++ b/src/ext/Util/wixext/Symbols/FileSharePermissionsSymbol.cs
@@ -0,0 +1,63 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Util
4{
5 using WixToolset.Data;
6 using WixToolset.Util.Symbols;
7
8 public static partial class UtilSymbolDefinitions
9 {
10 public static readonly IntermediateSymbolDefinition FileSharePermissions = new IntermediateSymbolDefinition(
11 UtilSymbolDefinitionType.FileSharePermissions.ToString(),
12 new[]
13 {
14 new IntermediateFieldDefinition(nameof(FileSharePermissionsSymbolFields.FileShareRef), IntermediateFieldType.String),
15 new IntermediateFieldDefinition(nameof(FileSharePermissionsSymbolFields.UserRef), IntermediateFieldType.String),
16 new IntermediateFieldDefinition(nameof(FileSharePermissionsSymbolFields.Permissions), IntermediateFieldType.Number),
17 },
18 typeof(FileSharePermissionsSymbol));
19 }
20}
21
22namespace WixToolset.Util.Symbols
23{
24 using WixToolset.Data;
25
26 public enum FileSharePermissionsSymbolFields
27 {
28 FileShareRef,
29 UserRef,
30 Permissions,
31 }
32
33 public class FileSharePermissionsSymbol : IntermediateSymbol
34 {
35 public FileSharePermissionsSymbol() : base(UtilSymbolDefinitions.FileSharePermissions, null, null)
36 {
37 }
38
39 public FileSharePermissionsSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(UtilSymbolDefinitions.FileSharePermissions, sourceLineNumber, id)
40 {
41 }
42
43 public IntermediateField this[FileSharePermissionsSymbolFields index] => this.Fields[(int)index];
44
45 public string FileShareRef
46 {
47 get => this.Fields[(int)FileSharePermissionsSymbolFields.FileShareRef].AsString();
48 set => this.Set((int)FileSharePermissionsSymbolFields.FileShareRef, value);
49 }
50
51 public string UserRef
52 {
53 get => this.Fields[(int)FileSharePermissionsSymbolFields.UserRef].AsString();
54 set => this.Set((int)FileSharePermissionsSymbolFields.UserRef, value);
55 }
56
57 public int Permissions
58 {
59 get => this.Fields[(int)FileSharePermissionsSymbolFields.Permissions].AsNumber();
60 set => this.Set((int)FileSharePermissionsSymbolFields.Permissions, value);
61 }
62 }
63} \ No newline at end of file
diff --git a/src/ext/Util/wixext/Symbols/FileShareSymbol.cs b/src/ext/Util/wixext/Symbols/FileShareSymbol.cs
new file mode 100644
index 00000000..c956ff42
--- /dev/null
+++ b/src/ext/Util/wixext/Symbols/FileShareSymbol.cs
@@ -0,0 +1,71 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Util
4{
5 using WixToolset.Data;
6 using WixToolset.Util.Symbols;
7
8 public static partial class UtilSymbolDefinitions
9 {
10 public static readonly IntermediateSymbolDefinition FileShare = new IntermediateSymbolDefinition(
11 UtilSymbolDefinitionType.FileShare.ToString(),
12 new[]
13 {
14 new IntermediateFieldDefinition(nameof(FileShareSymbolFields.ShareName), IntermediateFieldType.String),
15 new IntermediateFieldDefinition(nameof(FileShareSymbolFields.ComponentRef), IntermediateFieldType.String),
16 new IntermediateFieldDefinition(nameof(FileShareSymbolFields.Description), IntermediateFieldType.String),
17 new IntermediateFieldDefinition(nameof(FileShareSymbolFields.DirectoryRef), IntermediateFieldType.String),
18 },
19 typeof(FileShareSymbol));
20 }
21}
22
23namespace WixToolset.Util.Symbols
24{
25 using WixToolset.Data;
26
27 public enum FileShareSymbolFields
28 {
29 ShareName,
30 ComponentRef,
31 Description,
32 DirectoryRef,
33 }
34
35 public class FileShareSymbol : IntermediateSymbol
36 {
37 public FileShareSymbol() : base(UtilSymbolDefinitions.FileShare, null, null)
38 {
39 }
40
41 public FileShareSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(UtilSymbolDefinitions.FileShare, sourceLineNumber, id)
42 {
43 }
44
45 public IntermediateField this[FileShareSymbolFields index] => this.Fields[(int)index];
46
47 public string ShareName
48 {
49 get => this.Fields[(int)FileShareSymbolFields.ShareName].AsString();
50 set => this.Set((int)FileShareSymbolFields.ShareName, value);
51 }
52
53 public string ComponentRef
54 {
55 get => this.Fields[(int)FileShareSymbolFields.ComponentRef].AsString();
56 set => this.Set((int)FileShareSymbolFields.ComponentRef, value);
57 }
58
59 public string Description
60 {
61 get => this.Fields[(int)FileShareSymbolFields.Description].AsString();
62 set => this.Set((int)FileShareSymbolFields.Description, value);
63 }
64
65 public string DirectoryRef
66 {
67 get => this.Fields[(int)FileShareSymbolFields.DirectoryRef].AsString();
68 set => this.Set((int)FileShareSymbolFields.DirectoryRef, value);
69 }
70 }
71} \ No newline at end of file
diff --git a/src/ext/Util/wixext/Symbols/GroupSymbol.cs b/src/ext/Util/wixext/Symbols/GroupSymbol.cs
new file mode 100644
index 00000000..b378db44
--- /dev/null
+++ b/src/ext/Util/wixext/Symbols/GroupSymbol.cs
@@ -0,0 +1,63 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Util
4{
5 using WixToolset.Data;
6 using WixToolset.Util.Symbols;
7
8 public static partial class UtilSymbolDefinitions
9 {
10 public static readonly IntermediateSymbolDefinition Group = new IntermediateSymbolDefinition(
11 UtilSymbolDefinitionType.Group.ToString(),
12 new[]
13 {
14 new IntermediateFieldDefinition(nameof(GroupSymbolFields.ComponentRef), IntermediateFieldType.String),
15 new IntermediateFieldDefinition(nameof(GroupSymbolFields.Name), IntermediateFieldType.String),
16 new IntermediateFieldDefinition(nameof(GroupSymbolFields.Domain), IntermediateFieldType.String),
17 },
18 typeof(GroupSymbol));
19 }
20}
21
22namespace WixToolset.Util.Symbols
23{
24 using WixToolset.Data;
25
26 public enum GroupSymbolFields
27 {
28 ComponentRef,
29 Name,
30 Domain,
31 }
32
33 public class GroupSymbol : IntermediateSymbol
34 {
35 public GroupSymbol() : base(UtilSymbolDefinitions.Group, null, null)
36 {
37 }
38
39 public GroupSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(UtilSymbolDefinitions.Group, sourceLineNumber, id)
40 {
41 }
42
43 public IntermediateField this[GroupSymbolFields index] => this.Fields[(int)index];
44
45 public string ComponentRef
46 {
47 get => this.Fields[(int)GroupSymbolFields.ComponentRef].AsString();
48 set => this.Set((int)GroupSymbolFields.ComponentRef, value);
49 }
50
51 public string Name
52 {
53 get => this.Fields[(int)GroupSymbolFields.Name].AsString();
54 set => this.Set((int)GroupSymbolFields.Name, value);
55 }
56
57 public string Domain
58 {
59 get => this.Fields[(int)GroupSymbolFields.Domain].AsString();
60 set => this.Set((int)GroupSymbolFields.Domain, value);
61 }
62 }
63} \ No newline at end of file
diff --git a/src/ext/Util/wixext/Symbols/PerfmonManifestSymbol.cs b/src/ext/Util/wixext/Symbols/PerfmonManifestSymbol.cs
new file mode 100644
index 00000000..03fef14e
--- /dev/null
+++ b/src/ext/Util/wixext/Symbols/PerfmonManifestSymbol.cs
@@ -0,0 +1,63 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Util
4{
5 using WixToolset.Data;
6 using WixToolset.Util.Symbols;
7
8 public static partial class UtilSymbolDefinitions
9 {
10 public static readonly IntermediateSymbolDefinition PerfmonManifest = new IntermediateSymbolDefinition(
11 UtilSymbolDefinitionType.PerfmonManifest.ToString(),
12 new[]
13 {
14 new IntermediateFieldDefinition(nameof(PerfmonManifestSymbolFields.ComponentRef), IntermediateFieldType.String),
15 new IntermediateFieldDefinition(nameof(PerfmonManifestSymbolFields.File), IntermediateFieldType.String),
16 new IntermediateFieldDefinition(nameof(PerfmonManifestSymbolFields.ResourceFileDirectory), IntermediateFieldType.String),
17 },
18 typeof(PerfmonManifestSymbol));
19 }
20}
21
22namespace WixToolset.Util.Symbols
23{
24 using WixToolset.Data;
25
26 public enum PerfmonManifestSymbolFields
27 {
28 ComponentRef,
29 File,
30 ResourceFileDirectory,
31 }
32
33 public class PerfmonManifestSymbol : IntermediateSymbol
34 {
35 public PerfmonManifestSymbol() : base(UtilSymbolDefinitions.PerfmonManifest, null, null)
36 {
37 }
38
39 public PerfmonManifestSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(UtilSymbolDefinitions.PerfmonManifest, sourceLineNumber, id)
40 {
41 }
42
43 public IntermediateField this[PerfmonManifestSymbolFields index] => this.Fields[(int)index];
44
45 public string ComponentRef
46 {
47 get => this.Fields[(int)PerfmonManifestSymbolFields.ComponentRef].AsString();
48 set => this.Set((int)PerfmonManifestSymbolFields.ComponentRef, value);
49 }
50
51 public string File
52 {
53 get => this.Fields[(int)PerfmonManifestSymbolFields.File].AsString();
54 set => this.Set((int)PerfmonManifestSymbolFields.File, value);
55 }
56
57 public string ResourceFileDirectory
58 {
59 get => this.Fields[(int)PerfmonManifestSymbolFields.ResourceFileDirectory].AsString();
60 set => this.Set((int)PerfmonManifestSymbolFields.ResourceFileDirectory, value);
61 }
62 }
63} \ No newline at end of file
diff --git a/src/ext/Util/wixext/Symbols/PerfmonSymbol.cs b/src/ext/Util/wixext/Symbols/PerfmonSymbol.cs
new file mode 100644
index 00000000..6784ebd1
--- /dev/null
+++ b/src/ext/Util/wixext/Symbols/PerfmonSymbol.cs
@@ -0,0 +1,63 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Util
4{
5 using WixToolset.Data;
6 using WixToolset.Util.Symbols;
7
8 public static partial class UtilSymbolDefinitions
9 {
10 public static readonly IntermediateSymbolDefinition Perfmon = new IntermediateSymbolDefinition(
11 UtilSymbolDefinitionType.Perfmon.ToString(),
12 new[]
13 {
14 new IntermediateFieldDefinition(nameof(PerfmonSymbolFields.ComponentRef), IntermediateFieldType.String),
15 new IntermediateFieldDefinition(nameof(PerfmonSymbolFields.File), IntermediateFieldType.String),
16 new IntermediateFieldDefinition(nameof(PerfmonSymbolFields.Name), IntermediateFieldType.String),
17 },
18 typeof(PerfmonSymbol));
19 }
20}
21
22namespace WixToolset.Util.Symbols
23{
24 using WixToolset.Data;
25
26 public enum PerfmonSymbolFields
27 {
28 ComponentRef,
29 File,
30 Name,
31 }
32
33 public class PerfmonSymbol : IntermediateSymbol
34 {
35 public PerfmonSymbol() : base(UtilSymbolDefinitions.Perfmon, null, null)
36 {
37 }
38
39 public PerfmonSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(UtilSymbolDefinitions.Perfmon, sourceLineNumber, id)
40 {
41 }
42
43 public IntermediateField this[PerfmonSymbolFields index] => this.Fields[(int)index];
44
45 public string ComponentRef
46 {
47 get => this.Fields[(int)PerfmonSymbolFields.ComponentRef].AsString();
48 set => this.Set((int)PerfmonSymbolFields.ComponentRef, value);
49 }
50
51 public string File
52 {
53 get => this.Fields[(int)PerfmonSymbolFields.File].AsString();
54 set => this.Set((int)PerfmonSymbolFields.File, value);
55 }
56
57 public string Name
58 {
59 get => this.Fields[(int)PerfmonSymbolFields.Name].AsString();
60 set => this.Set((int)PerfmonSymbolFields.Name, value);
61 }
62 }
63} \ No newline at end of file
diff --git a/src/ext/Util/wixext/Symbols/PerformanceCategorySymbol.cs b/src/ext/Util/wixext/Symbols/PerformanceCategorySymbol.cs
new file mode 100644
index 00000000..5ecf388c
--- /dev/null
+++ b/src/ext/Util/wixext/Symbols/PerformanceCategorySymbol.cs
@@ -0,0 +1,71 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Util
4{
5 using WixToolset.Data;
6 using WixToolset.Util.Symbols;
7
8 public static partial class UtilSymbolDefinitions
9 {
10 public static readonly IntermediateSymbolDefinition PerformanceCategory = new IntermediateSymbolDefinition(
11 UtilSymbolDefinitionType.PerformanceCategory.ToString(),
12 new[]
13 {
14 new IntermediateFieldDefinition(nameof(PerformanceCategorySymbolFields.ComponentRef), IntermediateFieldType.String),
15 new IntermediateFieldDefinition(nameof(PerformanceCategorySymbolFields.Name), IntermediateFieldType.String),
16 new IntermediateFieldDefinition(nameof(PerformanceCategorySymbolFields.IniData), IntermediateFieldType.String),
17 new IntermediateFieldDefinition(nameof(PerformanceCategorySymbolFields.ConstantData), IntermediateFieldType.String),
18 },
19 typeof(PerformanceCategorySymbol));
20 }
21}
22
23namespace WixToolset.Util.Symbols
24{
25 using WixToolset.Data;
26
27 public enum PerformanceCategorySymbolFields
28 {
29 ComponentRef,
30 Name,
31 IniData,
32 ConstantData,
33 }
34
35 public class PerformanceCategorySymbol : IntermediateSymbol
36 {
37 public PerformanceCategorySymbol() : base(UtilSymbolDefinitions.PerformanceCategory, null, null)
38 {
39 }
40
41 public PerformanceCategorySymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(UtilSymbolDefinitions.PerformanceCategory, sourceLineNumber, id)
42 {
43 }
44
45 public IntermediateField this[PerformanceCategorySymbolFields index] => this.Fields[(int)index];
46
47 public string ComponentRef
48 {
49 get => this.Fields[(int)PerformanceCategorySymbolFields.ComponentRef].AsString();
50 set => this.Set((int)PerformanceCategorySymbolFields.ComponentRef, value);
51 }
52
53 public string Name
54 {
55 get => this.Fields[(int)PerformanceCategorySymbolFields.Name].AsString();
56 set => this.Set((int)PerformanceCategorySymbolFields.Name, value);
57 }
58
59 public string IniData
60 {
61 get => this.Fields[(int)PerformanceCategorySymbolFields.IniData].AsString();
62 set => this.Set((int)PerformanceCategorySymbolFields.IniData, value);
63 }
64
65 public string ConstantData
66 {
67 get => this.Fields[(int)PerformanceCategorySymbolFields.ConstantData].AsString();
68 set => this.Set((int)PerformanceCategorySymbolFields.ConstantData, value);
69 }
70 }
71} \ No newline at end of file
diff --git a/src/ext/Util/wixext/Symbols/SecureObjectsSymbol.cs b/src/ext/Util/wixext/Symbols/SecureObjectsSymbol.cs
new file mode 100644
index 00000000..25fc6dca
--- /dev/null
+++ b/src/ext/Util/wixext/Symbols/SecureObjectsSymbol.cs
@@ -0,0 +1,103 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Util
4{
5 using WixToolset.Data;
6 using WixToolset.Util.Symbols;
7
8 public static partial class UtilSymbolDefinitions
9 {
10 public static readonly IntermediateSymbolDefinition SecureObjects = new IntermediateSymbolDefinition(
11 UtilSymbolDefinitionType.SecureObjects.ToString(),
12 new[]
13 {
14 new IntermediateFieldDefinition(nameof(SecureObjectsSymbolFields.SecureObject), IntermediateFieldType.String),
15 new IntermediateFieldDefinition(nameof(SecureObjectsSymbolFields.Table), IntermediateFieldType.String),
16 new IntermediateFieldDefinition(nameof(SecureObjectsSymbolFields.Domain), IntermediateFieldType.String),
17 new IntermediateFieldDefinition(nameof(SecureObjectsSymbolFields.User), IntermediateFieldType.String),
18 new IntermediateFieldDefinition(nameof(SecureObjectsSymbolFields.Attributes), IntermediateFieldType.Number),
19 new IntermediateFieldDefinition(nameof(SecureObjectsSymbolFields.Permission), IntermediateFieldType.Number),
20 new IntermediateFieldDefinition(nameof(SecureObjectsSymbolFields.ComponentRef), IntermediateFieldType.String),
21 },
22 typeof(SecureObjectsSymbol));
23 }
24}
25
26namespace WixToolset.Util.Symbols
27{
28 using System;
29 using WixToolset.Data;
30
31 public enum SecureObjectsSymbolFields
32 {
33 SecureObject,
34 Table,
35 Domain,
36 User,
37 Attributes,
38 Permission,
39 ComponentRef,
40 }
41
42 [Flags]
43 public enum WixPermissionExAttributes
44 {
45 None = 0x0,
46 Inheritable = 0x01
47 }
48
49 public class SecureObjectsSymbol : IntermediateSymbol
50 {
51 public SecureObjectsSymbol() : base(UtilSymbolDefinitions.SecureObjects, null, null)
52 {
53 }
54
55 public SecureObjectsSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(UtilSymbolDefinitions.SecureObjects, sourceLineNumber, id)
56 {
57 }
58
59 public IntermediateField this[SecureObjectsSymbolFields index] => this.Fields[(int)index];
60
61 public string SecureObject
62 {
63 get => this.Fields[(int)SecureObjectsSymbolFields.SecureObject].AsString();
64 set => this.Set((int)SecureObjectsSymbolFields.SecureObject, value);
65 }
66
67 public string Table
68 {
69 get => this.Fields[(int)SecureObjectsSymbolFields.Table].AsString();
70 set => this.Set((int)SecureObjectsSymbolFields.Table, value);
71 }
72
73 public string Domain
74 {
75 get => this.Fields[(int)SecureObjectsSymbolFields.Domain].AsString();
76 set => this.Set((int)SecureObjectsSymbolFields.Domain, value);
77 }
78
79 public string User
80 {
81 get => this.Fields[(int)SecureObjectsSymbolFields.User].AsString();
82 set => this.Set((int)SecureObjectsSymbolFields.User, value);
83 }
84
85 public WixPermissionExAttributes Attributes
86 {
87 get => (WixPermissionExAttributes)this.Fields[(int)SecureObjectsSymbolFields.Attributes].AsNumber();
88 set => this.Set((int)SecureObjectsSymbolFields.Attributes, (int)value);
89 }
90
91 public int? Permission
92 {
93 get => this.Fields[(int)SecureObjectsSymbolFields.Permission].AsNullableNumber();
94 set => this.Set((int)SecureObjectsSymbolFields.Permission, value);
95 }
96
97 public string ComponentRef
98 {
99 get => this.Fields[(int)SecureObjectsSymbolFields.ComponentRef].AsString();
100 set => this.Set((int)SecureObjectsSymbolFields.ComponentRef, value);
101 }
102 }
103} \ No newline at end of file
diff --git a/src/ext/Util/wixext/Symbols/ServiceConfigSymbol.cs b/src/ext/Util/wixext/Symbols/ServiceConfigSymbol.cs
new file mode 100644
index 00000000..3a877f9b
--- /dev/null
+++ b/src/ext/Util/wixext/Symbols/ServiceConfigSymbol.cs
@@ -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
3namespace WixToolset.Util
4{
5 using WixToolset.Data;
6 using WixToolset.Util.Symbols;
7
8 public static partial class UtilSymbolDefinitions
9 {
10 public static readonly IntermediateSymbolDefinition ServiceConfig = new IntermediateSymbolDefinition(
11 UtilSymbolDefinitionType.ServiceConfig.ToString(),
12 new[]
13 {
14 new IntermediateFieldDefinition(nameof(ServiceConfigSymbolFields.ServiceName), IntermediateFieldType.String),
15 new IntermediateFieldDefinition(nameof(ServiceConfigSymbolFields.ComponentRef), IntermediateFieldType.String),
16 new IntermediateFieldDefinition(nameof(ServiceConfigSymbolFields.NewService), IntermediateFieldType.Number),
17 new IntermediateFieldDefinition(nameof(ServiceConfigSymbolFields.FirstFailureActionType), IntermediateFieldType.String),
18 new IntermediateFieldDefinition(nameof(ServiceConfigSymbolFields.SecondFailureActionType), IntermediateFieldType.String),
19 new IntermediateFieldDefinition(nameof(ServiceConfigSymbolFields.ThirdFailureActionType), IntermediateFieldType.String),
20 new IntermediateFieldDefinition(nameof(ServiceConfigSymbolFields.ResetPeriodInDays), IntermediateFieldType.Number),
21 new IntermediateFieldDefinition(nameof(ServiceConfigSymbolFields.RestartServiceDelayInSeconds), IntermediateFieldType.Number),
22 new IntermediateFieldDefinition(nameof(ServiceConfigSymbolFields.ProgramCommandLine), IntermediateFieldType.String),
23 new IntermediateFieldDefinition(nameof(ServiceConfigSymbolFields.RebootMessage), IntermediateFieldType.String),
24 },
25 typeof(ServiceConfigSymbol));
26 }
27}
28
29namespace WixToolset.Util.Symbols
30{
31 using WixToolset.Data;
32
33 public enum ServiceConfigSymbolFields
34 {
35 ServiceName,
36 ComponentRef,
37 NewService,
38 FirstFailureActionType,
39 SecondFailureActionType,
40 ThirdFailureActionType,
41 ResetPeriodInDays,
42 RestartServiceDelayInSeconds,
43 ProgramCommandLine,
44 RebootMessage,
45 }
46
47 public class ServiceConfigSymbol : IntermediateSymbol
48 {
49 public ServiceConfigSymbol() : base(UtilSymbolDefinitions.ServiceConfig, null, null)
50 {
51 }
52
53 public ServiceConfigSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(UtilSymbolDefinitions.ServiceConfig, sourceLineNumber, id)
54 {
55 }
56
57 public IntermediateField this[ServiceConfigSymbolFields index] => this.Fields[(int)index];
58
59 public string ServiceName
60 {
61 get => this.Fields[(int)ServiceConfigSymbolFields.ServiceName].AsString();
62 set => this.Set((int)ServiceConfigSymbolFields.ServiceName, value);
63 }
64
65 public string ComponentRef
66 {
67 get => this.Fields[(int)ServiceConfigSymbolFields.ComponentRef].AsString();
68 set => this.Set((int)ServiceConfigSymbolFields.ComponentRef, value);
69 }
70
71 public int NewService
72 {
73 get => this.Fields[(int)ServiceConfigSymbolFields.NewService].AsNumber();
74 set => this.Set((int)ServiceConfigSymbolFields.NewService, value);
75 }
76
77 public string FirstFailureActionType
78 {
79 get => this.Fields[(int)ServiceConfigSymbolFields.FirstFailureActionType].AsString();
80 set => this.Set((int)ServiceConfigSymbolFields.FirstFailureActionType, value);
81 }
82
83 public string SecondFailureActionType
84 {
85 get => this.Fields[(int)ServiceConfigSymbolFields.SecondFailureActionType].AsString();
86 set => this.Set((int)ServiceConfigSymbolFields.SecondFailureActionType, value);
87 }
88
89 public string ThirdFailureActionType
90 {
91 get => this.Fields[(int)ServiceConfigSymbolFields.ThirdFailureActionType].AsString();
92 set => this.Set((int)ServiceConfigSymbolFields.ThirdFailureActionType, value);
93 }
94
95 public int? ResetPeriodInDays
96 {
97 get => this.Fields[(int)ServiceConfigSymbolFields.ResetPeriodInDays].AsNullableNumber();
98 set => this.Set((int)ServiceConfigSymbolFields.ResetPeriodInDays, value);
99 }
100
101 public int? RestartServiceDelayInSeconds
102 {
103 get => this.Fields[(int)ServiceConfigSymbolFields.RestartServiceDelayInSeconds].AsNullableNumber();
104 set => this.Set((int)ServiceConfigSymbolFields.RestartServiceDelayInSeconds, value);
105 }
106
107 public string ProgramCommandLine
108 {
109 get => this.Fields[(int)ServiceConfigSymbolFields.ProgramCommandLine].AsString();
110 set => this.Set((int)ServiceConfigSymbolFields.ProgramCommandLine, value);
111 }
112
113 public string RebootMessage
114 {
115 get => this.Fields[(int)ServiceConfigSymbolFields.RebootMessage].AsString();
116 set => this.Set((int)ServiceConfigSymbolFields.RebootMessage, value);
117 }
118 }
119} \ No newline at end of file
diff --git a/src/ext/Util/wixext/Symbols/UserGroupSymbol.cs b/src/ext/Util/wixext/Symbols/UserGroupSymbol.cs
new file mode 100644
index 00000000..c8f3998e
--- /dev/null
+++ b/src/ext/Util/wixext/Symbols/UserGroupSymbol.cs
@@ -0,0 +1,55 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Util
4{
5 using WixToolset.Data;
6 using WixToolset.Util.Symbols;
7
8 public static partial class UtilSymbolDefinitions
9 {
10 public static readonly IntermediateSymbolDefinition UserGroup = new IntermediateSymbolDefinition(
11 UtilSymbolDefinitionType.UserGroup.ToString(),
12 new[]
13 {
14 new IntermediateFieldDefinition(nameof(UserGroupSymbolFields.UserRef), IntermediateFieldType.String),
15 new IntermediateFieldDefinition(nameof(UserGroupSymbolFields.GroupRef), IntermediateFieldType.String),
16 },
17 typeof(UserGroupSymbol));
18 }
19}
20
21namespace WixToolset.Util.Symbols
22{
23 using WixToolset.Data;
24
25 public enum UserGroupSymbolFields
26 {
27 UserRef,
28 GroupRef,
29 }
30
31 public class UserGroupSymbol : IntermediateSymbol
32 {
33 public UserGroupSymbol() : base(UtilSymbolDefinitions.UserGroup, null, null)
34 {
35 }
36
37 public UserGroupSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(UtilSymbolDefinitions.UserGroup, sourceLineNumber, id)
38 {
39 }
40
41 public IntermediateField this[UserGroupSymbolFields index] => this.Fields[(int)index];
42
43 public string UserRef
44 {
45 get => this.Fields[(int)UserGroupSymbolFields.UserRef].AsString();
46 set => this.Set((int)UserGroupSymbolFields.UserRef, value);
47 }
48
49 public string GroupRef
50 {
51 get => this.Fields[(int)UserGroupSymbolFields.GroupRef].AsString();
52 set => this.Set((int)UserGroupSymbolFields.GroupRef, value);
53 }
54 }
55} \ No newline at end of file
diff --git a/src/ext/Util/wixext/Symbols/UserSymbol.cs b/src/ext/Util/wixext/Symbols/UserSymbol.cs
new file mode 100644
index 00000000..5f00064b
--- /dev/null
+++ b/src/ext/Util/wixext/Symbols/UserSymbol.cs
@@ -0,0 +1,79 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Util
4{
5 using WixToolset.Data;
6 using WixToolset.Util.Symbols;
7
8 public static partial class UtilSymbolDefinitions
9 {
10 public static readonly IntermediateSymbolDefinition User = new IntermediateSymbolDefinition(
11 UtilSymbolDefinitionType.User.ToString(),
12 new[]
13 {
14 new IntermediateFieldDefinition(nameof(UserSymbolFields.ComponentRef), IntermediateFieldType.String),
15 new IntermediateFieldDefinition(nameof(UserSymbolFields.Name), IntermediateFieldType.String),
16 new IntermediateFieldDefinition(nameof(UserSymbolFields.Domain), IntermediateFieldType.String),
17 new IntermediateFieldDefinition(nameof(UserSymbolFields.Password), IntermediateFieldType.String),
18 new IntermediateFieldDefinition(nameof(UserSymbolFields.Attributes), IntermediateFieldType.Number),
19 },
20 typeof(UserSymbol));
21 }
22}
23
24namespace WixToolset.Util.Symbols
25{
26 using WixToolset.Data;
27
28 public enum UserSymbolFields
29 {
30 ComponentRef,
31 Name,
32 Domain,
33 Password,
34 Attributes,
35 }
36
37 public class UserSymbol : IntermediateSymbol
38 {
39 public UserSymbol() : base(UtilSymbolDefinitions.User, null, null)
40 {
41 }
42
43 public UserSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(UtilSymbolDefinitions.User, sourceLineNumber, id)
44 {
45 }
46
47 public IntermediateField this[UserSymbolFields index] => this.Fields[(int)index];
48
49 public string ComponentRef
50 {
51 get => this.Fields[(int)UserSymbolFields.ComponentRef].AsString();
52 set => this.Set((int)UserSymbolFields.ComponentRef, value);
53 }
54
55 public string Name
56 {
57 get => this.Fields[(int)UserSymbolFields.Name].AsString();
58 set => this.Set((int)UserSymbolFields.Name, value);
59 }
60
61 public string Domain
62 {
63 get => this.Fields[(int)UserSymbolFields.Domain].AsString();
64 set => this.Set((int)UserSymbolFields.Domain, value);
65 }
66
67 public string Password
68 {
69 get => this.Fields[(int)UserSymbolFields.Password].AsString();
70 set => this.Set((int)UserSymbolFields.Password, value);
71 }
72
73 public int Attributes
74 {
75 get => this.Fields[(int)UserSymbolFields.Attributes].AsNumber();
76 set => this.Set((int)UserSymbolFields.Attributes, value);
77 }
78 }
79} \ No newline at end of file
diff --git a/src/ext/Util/wixext/Symbols/UtilSymbolDefinitions.cs b/src/ext/Util/wixext/Symbols/UtilSymbolDefinitions.cs
new file mode 100644
index 00000000..72091c3b
--- /dev/null
+++ b/src/ext/Util/wixext/Symbols/UtilSymbolDefinitions.cs
@@ -0,0 +1,125 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Util
4{
5 using System;
6 using WixToolset.Data;
7 using WixToolset.Data.Burn;
8
9 public enum UtilSymbolDefinitionType
10 {
11 EventManifest,
12 FileShare,
13 FileSharePermissions,
14 Group,
15 Perfmon,
16 PerfmonManifest,
17 PerformanceCategory,
18 SecureObjects,
19 ServiceConfig,
20 User,
21 UserGroup,
22 WixCloseApplication,
23 WixFormatFiles,
24 WixInternetShortcut,
25 WixRemoveFolderEx,
26 WixRemoveRegistryKeyEx,
27 WixRestartResource,
28 WixTouchFile,
29 WixWindowsFeatureSearch,
30 XmlConfig,
31 XmlFile,
32 }
33
34 public static partial class UtilSymbolDefinitions
35 {
36 public static readonly Version Version = new Version("4.0.0");
37
38 public static IntermediateSymbolDefinition ByName(string name)
39 {
40 if (!Enum.TryParse(name, out UtilSymbolDefinitionType type))
41 {
42 return null;
43 }
44
45 return ByType(type);
46 }
47
48 public static IntermediateSymbolDefinition ByType(UtilSymbolDefinitionType type)
49 {
50 switch (type)
51 {
52 case UtilSymbolDefinitionType.EventManifest:
53 return UtilSymbolDefinitions.EventManifest;
54
55 case UtilSymbolDefinitionType.FileShare:
56 return UtilSymbolDefinitions.FileShare;
57
58 case UtilSymbolDefinitionType.FileSharePermissions:
59 return UtilSymbolDefinitions.FileSharePermissions;
60
61 case UtilSymbolDefinitionType.Group:
62 return UtilSymbolDefinitions.Group;
63
64 case UtilSymbolDefinitionType.Perfmon:
65 return UtilSymbolDefinitions.Perfmon;
66
67 case UtilSymbolDefinitionType.PerfmonManifest:
68 return UtilSymbolDefinitions.PerfmonManifest;
69
70 case UtilSymbolDefinitionType.PerformanceCategory:
71 return UtilSymbolDefinitions.PerformanceCategory;
72
73 case UtilSymbolDefinitionType.SecureObjects:
74 return UtilSymbolDefinitions.SecureObjects;
75
76 case UtilSymbolDefinitionType.ServiceConfig:
77 return UtilSymbolDefinitions.ServiceConfig;
78
79 case UtilSymbolDefinitionType.User:
80 return UtilSymbolDefinitions.User;
81
82 case UtilSymbolDefinitionType.UserGroup:
83 return UtilSymbolDefinitions.UserGroup;
84
85 case UtilSymbolDefinitionType.WixCloseApplication:
86 return UtilSymbolDefinitions.WixCloseApplication;
87
88 case UtilSymbolDefinitionType.WixFormatFiles:
89 return UtilSymbolDefinitions.WixFormatFiles;
90
91 case UtilSymbolDefinitionType.WixInternetShortcut:
92 return UtilSymbolDefinitions.WixInternetShortcut;
93
94 case UtilSymbolDefinitionType.WixRemoveFolderEx:
95 return UtilSymbolDefinitions.WixRemoveFolderEx;
96
97 case UtilSymbolDefinitionType.WixRemoveRegistryKeyEx:
98 return UtilSymbolDefinitions.WixRemoveRegistryKeyEx;
99
100 case UtilSymbolDefinitionType.WixRestartResource:
101 return UtilSymbolDefinitions.WixRestartResource;
102
103 case UtilSymbolDefinitionType.WixTouchFile:
104 return UtilSymbolDefinitions.WixTouchFile;
105
106 case UtilSymbolDefinitionType.WixWindowsFeatureSearch:
107 return UtilSymbolDefinitions.WixWindowsFeatureSearch;
108
109 case UtilSymbolDefinitionType.XmlConfig:
110 return UtilSymbolDefinitions.XmlConfig;
111
112 case UtilSymbolDefinitionType.XmlFile:
113 return UtilSymbolDefinitions.XmlFile;
114
115 default:
116 throw new ArgumentOutOfRangeException(nameof(type));
117 }
118 }
119
120 static UtilSymbolDefinitions()
121 {
122 WixWindowsFeatureSearch.AddTag(BurnConstants.BundleExtensionSearchSymbolDefinitionTag);
123 }
124 }
125}
diff --git a/src/ext/Util/wixext/Symbols/WixCloseApplicationSymbol.cs b/src/ext/Util/wixext/Symbols/WixCloseApplicationSymbol.cs
new file mode 100644
index 00000000..0738e3e4
--- /dev/null
+++ b/src/ext/Util/wixext/Symbols/WixCloseApplicationSymbol.cs
@@ -0,0 +1,103 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Util
4{
5 using WixToolset.Data;
6 using WixToolset.Util.Symbols;
7
8 public static partial class UtilSymbolDefinitions
9 {
10 public static readonly IntermediateSymbolDefinition WixCloseApplication = new IntermediateSymbolDefinition(
11 UtilSymbolDefinitionType.WixCloseApplication.ToString(),
12 new[]
13 {
14 new IntermediateFieldDefinition(nameof(WixCloseApplicationSymbolFields.Target), IntermediateFieldType.String),
15 new IntermediateFieldDefinition(nameof(WixCloseApplicationSymbolFields.Description), IntermediateFieldType.String),
16 new IntermediateFieldDefinition(nameof(WixCloseApplicationSymbolFields.Condition), IntermediateFieldType.String),
17 new IntermediateFieldDefinition(nameof(WixCloseApplicationSymbolFields.Attributes), IntermediateFieldType.Number),
18 new IntermediateFieldDefinition(nameof(WixCloseApplicationSymbolFields.Sequence), IntermediateFieldType.Number),
19 new IntermediateFieldDefinition(nameof(WixCloseApplicationSymbolFields.Property), IntermediateFieldType.String),
20 new IntermediateFieldDefinition(nameof(WixCloseApplicationSymbolFields.TerminateExitCode), IntermediateFieldType.Number),
21 new IntermediateFieldDefinition(nameof(WixCloseApplicationSymbolFields.Timeout), IntermediateFieldType.Number),
22 },
23 typeof(WixCloseApplicationSymbol));
24 }
25}
26
27namespace WixToolset.Util.Symbols
28{
29 using WixToolset.Data;
30
31 public enum WixCloseApplicationSymbolFields
32 {
33 Target,
34 Description,
35 Condition,
36 Attributes,
37 Sequence,
38 Property,
39 TerminateExitCode,
40 Timeout,
41 }
42
43 public class WixCloseApplicationSymbol : IntermediateSymbol
44 {
45 public WixCloseApplicationSymbol() : base(UtilSymbolDefinitions.WixCloseApplication, null, null)
46 {
47 }
48
49 public WixCloseApplicationSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(UtilSymbolDefinitions.WixCloseApplication, sourceLineNumber, id)
50 {
51 }
52
53 public IntermediateField this[WixCloseApplicationSymbolFields index] => this.Fields[(int)index];
54
55 public string Target
56 {
57 get => this.Fields[(int)WixCloseApplicationSymbolFields.Target].AsString();
58 set => this.Set((int)WixCloseApplicationSymbolFields.Target, value);
59 }
60
61 public string Description
62 {
63 get => this.Fields[(int)WixCloseApplicationSymbolFields.Description].AsString();
64 set => this.Set((int)WixCloseApplicationSymbolFields.Description, value);
65 }
66
67 public string Condition
68 {
69 get => this.Fields[(int)WixCloseApplicationSymbolFields.Condition].AsString();
70 set => this.Set((int)WixCloseApplicationSymbolFields.Condition, value);
71 }
72
73 public int Attributes
74 {
75 get => this.Fields[(int)WixCloseApplicationSymbolFields.Attributes].AsNumber();
76 set => this.Set((int)WixCloseApplicationSymbolFields.Attributes, value);
77 }
78
79 public int? Sequence
80 {
81 get => this.Fields[(int)WixCloseApplicationSymbolFields.Sequence].AsNullableNumber();
82 set => this.Set((int)WixCloseApplicationSymbolFields.Sequence, value);
83 }
84
85 public string Property
86 {
87 get => this.Fields[(int)WixCloseApplicationSymbolFields.Property].AsString();
88 set => this.Set((int)WixCloseApplicationSymbolFields.Property, value);
89 }
90
91 public int? TerminateExitCode
92 {
93 get => this.Fields[(int)WixCloseApplicationSymbolFields.TerminateExitCode].AsNullableNumber();
94 set => this.Set((int)WixCloseApplicationSymbolFields.TerminateExitCode, value);
95 }
96
97 public int? Timeout
98 {
99 get => this.Fields[(int)WixCloseApplicationSymbolFields.Timeout].AsNullableNumber();
100 set => this.Set((int)WixCloseApplicationSymbolFields.Timeout, value);
101 }
102 }
103} \ No newline at end of file
diff --git a/src/ext/Util/wixext/Symbols/WixFormatFilesSymbol.cs b/src/ext/Util/wixext/Symbols/WixFormatFilesSymbol.cs
new file mode 100644
index 00000000..38a9b8ff
--- /dev/null
+++ b/src/ext/Util/wixext/Symbols/WixFormatFilesSymbol.cs
@@ -0,0 +1,55 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Util
4{
5 using WixToolset.Data;
6 using WixToolset.Util.Symbols;
7
8 public static partial class UtilSymbolDefinitions
9 {
10 public static readonly IntermediateSymbolDefinition WixFormatFiles = new IntermediateSymbolDefinition(
11 UtilSymbolDefinitionType.WixFormatFiles.ToString(),
12 new[]
13 {
14 new IntermediateFieldDefinition(nameof(WixFormatFilesSymbolFields.BinaryRef), IntermediateFieldType.String),
15 new IntermediateFieldDefinition(nameof(WixFormatFilesSymbolFields.FileRef), IntermediateFieldType.String),
16 },
17 typeof(WixFormatFilesSymbol));
18 }
19}
20
21namespace WixToolset.Util.Symbols
22{
23 using WixToolset.Data;
24
25 public enum WixFormatFilesSymbolFields
26 {
27 BinaryRef,
28 FileRef,
29 }
30
31 public class WixFormatFilesSymbol : IntermediateSymbol
32 {
33 public WixFormatFilesSymbol() : base(UtilSymbolDefinitions.WixFormatFiles, null, null)
34 {
35 }
36
37 public WixFormatFilesSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(UtilSymbolDefinitions.WixFormatFiles, sourceLineNumber, id)
38 {
39 }
40
41 public IntermediateField this[WixFormatFilesSymbolFields index] => this.Fields[(int)index];
42
43 public string BinaryRef
44 {
45 get => this.Fields[(int)WixFormatFilesSymbolFields.BinaryRef].AsString();
46 set => this.Set((int)WixFormatFilesSymbolFields.BinaryRef, value);
47 }
48
49 public string FileRef
50 {
51 get => this.Fields[(int)WixFormatFilesSymbolFields.FileRef].AsString();
52 set => this.Set((int)WixFormatFilesSymbolFields.FileRef, value);
53 }
54 }
55} \ No newline at end of file
diff --git a/src/ext/Util/wixext/Symbols/WixInternetShortcutSymbol.cs b/src/ext/Util/wixext/Symbols/WixInternetShortcutSymbol.cs
new file mode 100644
index 00000000..e8265e02
--- /dev/null
+++ b/src/ext/Util/wixext/Symbols/WixInternetShortcutSymbol.cs
@@ -0,0 +1,95 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Util
4{
5 using WixToolset.Data;
6 using WixToolset.Util.Symbols;
7
8 public static partial class UtilSymbolDefinitions
9 {
10 public static readonly IntermediateSymbolDefinition WixInternetShortcut = new IntermediateSymbolDefinition(
11 UtilSymbolDefinitionType.WixInternetShortcut.ToString(),
12 new[]
13 {
14 new IntermediateFieldDefinition(nameof(WixInternetShortcutSymbolFields.ComponentRef), IntermediateFieldType.String),
15 new IntermediateFieldDefinition(nameof(WixInternetShortcutSymbolFields.DirectoryRef), IntermediateFieldType.String),
16 new IntermediateFieldDefinition(nameof(WixInternetShortcutSymbolFields.Name), IntermediateFieldType.String),
17 new IntermediateFieldDefinition(nameof(WixInternetShortcutSymbolFields.Target), IntermediateFieldType.String),
18 new IntermediateFieldDefinition(nameof(WixInternetShortcutSymbolFields.Attributes), IntermediateFieldType.Number),
19 new IntermediateFieldDefinition(nameof(WixInternetShortcutSymbolFields.IconFile), IntermediateFieldType.String),
20 new IntermediateFieldDefinition(nameof(WixInternetShortcutSymbolFields.IconIndex), IntermediateFieldType.Number),
21 },
22 typeof(WixInternetShortcutSymbol));
23 }
24}
25
26namespace WixToolset.Util.Symbols
27{
28 using WixToolset.Data;
29
30 public enum WixInternetShortcutSymbolFields
31 {
32 ComponentRef,
33 DirectoryRef,
34 Name,
35 Target,
36 Attributes,
37 IconFile,
38 IconIndex,
39 }
40
41 public class WixInternetShortcutSymbol : IntermediateSymbol
42 {
43 public WixInternetShortcutSymbol() : base(UtilSymbolDefinitions.WixInternetShortcut, null, null)
44 {
45 }
46
47 public WixInternetShortcutSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(UtilSymbolDefinitions.WixInternetShortcut, sourceLineNumber, id)
48 {
49 }
50
51 public IntermediateField this[WixInternetShortcutSymbolFields index] => this.Fields[(int)index];
52
53 public string ComponentRef
54 {
55 get => this.Fields[(int)WixInternetShortcutSymbolFields.ComponentRef].AsString();
56 set => this.Set((int)WixInternetShortcutSymbolFields.ComponentRef, value);
57 }
58
59 public string DirectoryRef
60 {
61 get => this.Fields[(int)WixInternetShortcutSymbolFields.DirectoryRef].AsString();
62 set => this.Set((int)WixInternetShortcutSymbolFields.DirectoryRef, value);
63 }
64
65 public string Name
66 {
67 get => this.Fields[(int)WixInternetShortcutSymbolFields.Name].AsString();
68 set => this.Set((int)WixInternetShortcutSymbolFields.Name, value);
69 }
70
71 public string Target
72 {
73 get => this.Fields[(int)WixInternetShortcutSymbolFields.Target].AsString();
74 set => this.Set((int)WixInternetShortcutSymbolFields.Target, value);
75 }
76
77 public int Attributes
78 {
79 get => this.Fields[(int)WixInternetShortcutSymbolFields.Attributes].AsNumber();
80 set => this.Set((int)WixInternetShortcutSymbolFields.Attributes, value);
81 }
82
83 public string IconFile
84 {
85 get => this.Fields[(int)WixInternetShortcutSymbolFields.IconFile].AsString();
86 set => this.Set((int)WixInternetShortcutSymbolFields.IconFile, value);
87 }
88
89 public int? IconIndex
90 {
91 get => this.Fields[(int)WixInternetShortcutSymbolFields.IconIndex].AsNullableNumber();
92 set => this.Set((int)WixInternetShortcutSymbolFields.IconIndex, value);
93 }
94 }
95} \ No newline at end of file
diff --git a/src/ext/Util/wixext/Symbols/WixRemoveFolderExSymbol.cs b/src/ext/Util/wixext/Symbols/WixRemoveFolderExSymbol.cs
new file mode 100644
index 00000000..86352b6c
--- /dev/null
+++ b/src/ext/Util/wixext/Symbols/WixRemoveFolderExSymbol.cs
@@ -0,0 +1,78 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Util
4{
5 using WixToolset.Data;
6 using WixToolset.Util.Symbols;
7
8 public static partial class UtilSymbolDefinitions
9 {
10 public static readonly IntermediateSymbolDefinition WixRemoveFolderEx = new IntermediateSymbolDefinition(
11 UtilSymbolDefinitionType.WixRemoveFolderEx.ToString(),
12 new[]
13 {
14 new IntermediateFieldDefinition(nameof(WixRemoveFolderExSymbolFields.ComponentRef), IntermediateFieldType.String),
15 new IntermediateFieldDefinition(nameof(WixRemoveFolderExSymbolFields.Property), IntermediateFieldType.String),
16 new IntermediateFieldDefinition(nameof(WixRemoveFolderExSymbolFields.InstallMode), IntermediateFieldType.Number),
17 new IntermediateFieldDefinition(nameof(WixRemoveFolderExSymbolFields.Condition), IntermediateFieldType.String),
18 },
19 typeof(WixRemoveFolderExSymbol));
20 }
21}
22
23namespace WixToolset.Util.Symbols
24{
25 using WixToolset.Data;
26
27 public enum WixRemoveFolderExSymbolFields
28 {
29 ComponentRef,
30 Property,
31 InstallMode,
32 Condition,
33 }
34
35 public enum WixRemoveFolderExInstallMode
36 {
37 Install = 1,
38 Uninstall = 2,
39 Both = 3,
40 }
41
42 public class WixRemoveFolderExSymbol : IntermediateSymbol
43 {
44 public WixRemoveFolderExSymbol() : base(UtilSymbolDefinitions.WixRemoveFolderEx, null, null)
45 {
46 }
47
48 public WixRemoveFolderExSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(UtilSymbolDefinitions.WixRemoveFolderEx, sourceLineNumber, id)
49 {
50 }
51
52 public IntermediateField this[WixRemoveFolderExSymbolFields index] => this.Fields[(int)index];
53
54 public string ComponentRef
55 {
56 get => this.Fields[(int)WixRemoveFolderExSymbolFields.ComponentRef].AsString();
57 set => this.Set((int)WixRemoveFolderExSymbolFields.ComponentRef, value);
58 }
59
60 public string Property
61 {
62 get => this.Fields[(int)WixRemoveFolderExSymbolFields.Property].AsString();
63 set => this.Set((int)WixRemoveFolderExSymbolFields.Property, value);
64 }
65
66 public WixRemoveFolderExInstallMode InstallMode
67 {
68 get => (WixRemoveFolderExInstallMode)this.Fields[(int)WixRemoveFolderExSymbolFields.InstallMode].AsNumber();
69 set => this.Set((int)WixRemoveFolderExSymbolFields.InstallMode, (int)value);
70 }
71
72 public string Condition
73 {
74 get => this.Fields[(int)WixRemoveFolderExSymbolFields.Condition].AsString();
75 set => this.Set((int)WixRemoveFolderExSymbolFields.Condition, value);
76 }
77 }
78} \ No newline at end of file
diff --git a/src/ext/Util/wixext/Symbols/WixRemoveRegistryKeyExSymbol.cs b/src/ext/Util/wixext/Symbols/WixRemoveRegistryKeyExSymbol.cs
new file mode 100644
index 00000000..8e4bd212
--- /dev/null
+++ b/src/ext/Util/wixext/Symbols/WixRemoveRegistryKeyExSymbol.cs
@@ -0,0 +1,86 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Util
4{
5 using WixToolset.Data;
6 using WixToolset.Util.Symbols;
7
8 public static partial class UtilSymbolDefinitions
9 {
10 public static readonly IntermediateSymbolDefinition WixRemoveRegistryKeyEx = new IntermediateSymbolDefinition(
11 UtilSymbolDefinitionType.WixRemoveRegistryKeyEx.ToString(),
12 new[]
13 {
14 new IntermediateFieldDefinition(nameof(WixRemoveRegistryKeyExSymbolFields.ComponentRef), IntermediateFieldType.String),
15 new IntermediateFieldDefinition(nameof(WixRemoveRegistryKeyExSymbolFields.Root), IntermediateFieldType.Number),
16 new IntermediateFieldDefinition(nameof(WixRemoveRegistryKeyExSymbolFields.Key), IntermediateFieldType.String),
17 new IntermediateFieldDefinition(nameof(WixRemoveRegistryKeyExSymbolFields.InstallMode), IntermediateFieldType.Number),
18 new IntermediateFieldDefinition(nameof(WixRemoveRegistryKeyExSymbolFields.Condition), IntermediateFieldType.String),
19 },
20 typeof(WixRemoveRegistryKeyExSymbol));
21 }
22}
23
24namespace WixToolset.Util.Symbols
25{
26 using WixToolset.Data;
27 using WixToolset.Data.Symbols;
28
29 public enum WixRemoveRegistryKeyExSymbolFields
30 {
31 ComponentRef,
32 Root,
33 Key,
34 InstallMode,
35 Condition,
36 }
37
38 public enum WixRemoveRegistryKeyExInstallMode
39 {
40 Install = 1,
41 Uninstall = 2,
42 }
43
44 public class WixRemoveRegistryKeyExSymbol : IntermediateSymbol
45 {
46 public WixRemoveRegistryKeyExSymbol() : base(UtilSymbolDefinitions.WixRemoveRegistryKeyEx, null, null)
47 {
48 }
49
50 public WixRemoveRegistryKeyExSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(UtilSymbolDefinitions.WixRemoveRegistryKeyEx, sourceLineNumber, id)
51 {
52 }
53
54 public IntermediateField this[WixRemoveRegistryKeyExSymbolFields index] => this.Fields[(int)index];
55
56 public string ComponentRef
57 {
58 get => this.Fields[(int)WixRemoveRegistryKeyExSymbolFields.ComponentRef].AsString();
59 set => this.Set((int)WixRemoveRegistryKeyExSymbolFields.ComponentRef, value);
60 }
61
62 public RegistryRootType Root
63 {
64 get => (RegistryRootType)this.Fields[(int)WixRemoveRegistryKeyExSymbolFields.Root].AsNumber();
65 set => this.Set((int)WixRemoveRegistryKeyExSymbolFields.Root, (int)value);
66 }
67
68 public string Key
69 {
70 get => (string)this.Fields[(int)WixRemoveRegistryKeyExSymbolFields.Key];
71 set => this.Set((int)WixRemoveRegistryKeyExSymbolFields.Key, value);
72 }
73
74 public WixRemoveRegistryKeyExInstallMode InstallMode
75 {
76 get => (WixRemoveRegistryKeyExInstallMode)this.Fields[(int)WixRemoveRegistryKeyExSymbolFields.InstallMode].AsNumber();
77 set => this.Set((int)WixRemoveRegistryKeyExSymbolFields.InstallMode, (int)value);
78 }
79
80 public string Condition
81 {
82 get => this.Fields[(int)WixRemoveRegistryKeyExSymbolFields.Condition].AsString();
83 set => this.Set((int)WixRemoveRegistryKeyExSymbolFields.Condition, value);
84 }
85 }
86}
diff --git a/src/ext/Util/wixext/Symbols/WixRestartResourceSymbol.cs b/src/ext/Util/wixext/Symbols/WixRestartResourceSymbol.cs
new file mode 100644
index 00000000..01b92b63
--- /dev/null
+++ b/src/ext/Util/wixext/Symbols/WixRestartResourceSymbol.cs
@@ -0,0 +1,71 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Util
4{
5 using WixToolset.Data;
6 using WixToolset.Util.Symbols;
7
8 public static partial class UtilSymbolDefinitions
9 {
10 public static readonly IntermediateSymbolDefinition WixRestartResource = new IntermediateSymbolDefinition(
11 UtilSymbolDefinitionType.WixRestartResource.ToString(),
12 new[]
13 {
14 new IntermediateFieldDefinition(nameof(WixRestartResourceSymbolFields.ComponentRef), IntermediateFieldType.String),
15 new IntermediateFieldDefinition(nameof(WixRestartResourceSymbolFields.Resource), IntermediateFieldType.String),
16 new IntermediateFieldDefinition(nameof(WixRestartResourceSymbolFields.Attributes), IntermediateFieldType.Number),
17 },
18 typeof(WixRestartResourceSymbol));
19 }
20}
21
22namespace WixToolset.Util.Symbols
23{
24 using WixToolset.Data;
25
26 public enum WixRestartResourceSymbolFields
27 {
28 ComponentRef,
29 Resource,
30 Attributes,
31 }
32
33 public enum WixRestartResourceAttributes
34 {
35 Filename = 1,
36 ProcessName,
37 ServiceName,
38 TypeMask = 0xf,
39 }
40
41 public class WixRestartResourceSymbol : IntermediateSymbol
42 {
43 public WixRestartResourceSymbol() : base(UtilSymbolDefinitions.WixRestartResource, null, null)
44 {
45 }
46
47 public WixRestartResourceSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(UtilSymbolDefinitions.WixRestartResource, sourceLineNumber, id)
48 {
49 }
50
51 public IntermediateField this[WixRestartResourceSymbolFields index] => this.Fields[(int)index];
52
53 public string ComponentRef
54 {
55 get => this.Fields[(int)WixRestartResourceSymbolFields.ComponentRef].AsString();
56 set => this.Set((int)WixRestartResourceSymbolFields.ComponentRef, value);
57 }
58
59 public string Resource
60 {
61 get => this.Fields[(int)WixRestartResourceSymbolFields.Resource].AsString();
62 set => this.Set((int)WixRestartResourceSymbolFields.Resource, value);
63 }
64
65 public WixRestartResourceAttributes? Attributes
66 {
67 get => (WixRestartResourceAttributes?)this.Fields[(int)WixRestartResourceSymbolFields.Attributes].AsNullableNumber();
68 set => this.Set((int)WixRestartResourceSymbolFields.Attributes, (int?)value);
69 }
70 }
71} \ No newline at end of file
diff --git a/src/ext/Util/wixext/Symbols/WixTouchFileSymbol.cs b/src/ext/Util/wixext/Symbols/WixTouchFileSymbol.cs
new file mode 100644
index 00000000..447c21ba
--- /dev/null
+++ b/src/ext/Util/wixext/Symbols/WixTouchFileSymbol.cs
@@ -0,0 +1,63 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Util
4{
5 using WixToolset.Data;
6 using WixToolset.Util.Symbols;
7
8 public static partial class UtilSymbolDefinitions
9 {
10 public static readonly IntermediateSymbolDefinition WixTouchFile = new IntermediateSymbolDefinition(
11 UtilSymbolDefinitionType.WixTouchFile.ToString(),
12 new[]
13 {
14 new IntermediateFieldDefinition(nameof(WixTouchFileSymbolFields.ComponentRef), IntermediateFieldType.String),
15 new IntermediateFieldDefinition(nameof(WixTouchFileSymbolFields.Path), IntermediateFieldType.String),
16 new IntermediateFieldDefinition(nameof(WixTouchFileSymbolFields.Attributes), IntermediateFieldType.Number),
17 },
18 typeof(WixTouchFileSymbol));
19 }
20}
21
22namespace WixToolset.Util.Symbols
23{
24 using WixToolset.Data;
25
26 public enum WixTouchFileSymbolFields
27 {
28 ComponentRef,
29 Path,
30 Attributes,
31 }
32
33 public class WixTouchFileSymbol : IntermediateSymbol
34 {
35 public WixTouchFileSymbol() : base(UtilSymbolDefinitions.WixTouchFile, null, null)
36 {
37 }
38
39 public WixTouchFileSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(UtilSymbolDefinitions.WixTouchFile, sourceLineNumber, id)
40 {
41 }
42
43 public IntermediateField this[WixTouchFileSymbolFields index] => this.Fields[(int)index];
44
45 public string ComponentRef
46 {
47 get => this.Fields[(int)WixTouchFileSymbolFields.ComponentRef].AsString();
48 set => this.Set((int)WixTouchFileSymbolFields.ComponentRef, value);
49 }
50
51 public string Path
52 {
53 get => this.Fields[(int)WixTouchFileSymbolFields.Path].AsString();
54 set => this.Set((int)WixTouchFileSymbolFields.Path, value);
55 }
56
57 public int Attributes
58 {
59 get => this.Fields[(int)WixTouchFileSymbolFields.Attributes].AsNumber();
60 set => this.Set((int)WixTouchFileSymbolFields.Attributes, value);
61 }
62 }
63} \ No newline at end of file
diff --git a/src/ext/Util/wixext/Symbols/WixWindowsFeatureSearchSymbol.cs b/src/ext/Util/wixext/Symbols/WixWindowsFeatureSearchSymbol.cs
new file mode 100644
index 00000000..9a43692c
--- /dev/null
+++ b/src/ext/Util/wixext/Symbols/WixWindowsFeatureSearchSymbol.cs
@@ -0,0 +1,47 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Util
4{
5 using WixToolset.Data;
6 using WixToolset.Util.Symbols;
7
8 public static partial class UtilSymbolDefinitions
9 {
10 public static readonly IntermediateSymbolDefinition WixWindowsFeatureSearch = new IntermediateSymbolDefinition(
11 UtilSymbolDefinitionType.WixWindowsFeatureSearch.ToString(),
12 new[]
13 {
14 new IntermediateFieldDefinition(nameof(WixWindowsFeatureSearchSymbolFields.Type), IntermediateFieldType.String),
15 },
16 typeof(WixWindowsFeatureSearchSymbol));
17 }
18}
19
20namespace WixToolset.Util.Symbols
21{
22 using WixToolset.Data;
23
24 public enum WixWindowsFeatureSearchSymbolFields
25 {
26 Type,
27 }
28
29 public class WixWindowsFeatureSearchSymbol : IntermediateSymbol
30 {
31 public WixWindowsFeatureSearchSymbol() : base(UtilSymbolDefinitions.WixWindowsFeatureSearch, null, null)
32 {
33 }
34
35 public WixWindowsFeatureSearchSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(UtilSymbolDefinitions.WixWindowsFeatureSearch, sourceLineNumber, id)
36 {
37 }
38
39 public IntermediateField this[WixWindowsFeatureSearchSymbolFields index] => this.Fields[(int)index];
40
41 public string Type
42 {
43 get => this.Fields[(int)WixWindowsFeatureSearchSymbolFields.Type].AsString();
44 set => this.Set((int)WixWindowsFeatureSearchSymbolFields.Type, value);
45 }
46 }
47}
diff --git a/src/ext/Util/wixext/Symbols/XmlConfigSymbol.cs b/src/ext/Util/wixext/Symbols/XmlConfigSymbol.cs
new file mode 100644
index 00000000..6503a586
--- /dev/null
+++ b/src/ext/Util/wixext/Symbols/XmlConfigSymbol.cs
@@ -0,0 +1,111 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Util
4{
5 using WixToolset.Data;
6 using WixToolset.Util.Symbols;
7
8 public static partial class UtilSymbolDefinitions
9 {
10 public static readonly IntermediateSymbolDefinition XmlConfig = new IntermediateSymbolDefinition(
11 UtilSymbolDefinitionType.XmlConfig.ToString(),
12 new[]
13 {
14 new IntermediateFieldDefinition(nameof(XmlConfigSymbolFields.File), IntermediateFieldType.String),
15 new IntermediateFieldDefinition(nameof(XmlConfigSymbolFields.ElementId), IntermediateFieldType.String),
16 new IntermediateFieldDefinition(nameof(XmlConfigSymbolFields.ElementPath), IntermediateFieldType.String),
17 new IntermediateFieldDefinition(nameof(XmlConfigSymbolFields.VerifyPath), IntermediateFieldType.String),
18 new IntermediateFieldDefinition(nameof(XmlConfigSymbolFields.Name), IntermediateFieldType.String),
19 new IntermediateFieldDefinition(nameof(XmlConfigSymbolFields.Value), IntermediateFieldType.String),
20 new IntermediateFieldDefinition(nameof(XmlConfigSymbolFields.Flags), IntermediateFieldType.Number),
21 new IntermediateFieldDefinition(nameof(XmlConfigSymbolFields.ComponentRef), IntermediateFieldType.String),
22 new IntermediateFieldDefinition(nameof(XmlConfigSymbolFields.Sequence), IntermediateFieldType.Number),
23 },
24 typeof(XmlConfigSymbol));
25 }
26}
27
28namespace WixToolset.Util.Symbols
29{
30 using WixToolset.Data;
31
32 public enum XmlConfigSymbolFields
33 {
34 File,
35 ElementId,
36 ElementPath,
37 VerifyPath,
38 Name,
39 Value,
40 Flags,
41 ComponentRef,
42 Sequence,
43 }
44
45 public class XmlConfigSymbol : IntermediateSymbol
46 {
47 public XmlConfigSymbol() : base(UtilSymbolDefinitions.XmlConfig, null, null)
48 {
49 }
50
51 public XmlConfigSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(UtilSymbolDefinitions.XmlConfig, sourceLineNumber, id)
52 {
53 }
54
55 public IntermediateField this[XmlConfigSymbolFields index] => this.Fields[(int)index];
56
57 public string File
58 {
59 get => this.Fields[(int)XmlConfigSymbolFields.File].AsString();
60 set => this.Set((int)XmlConfigSymbolFields.File, value);
61 }
62
63 public string ElementId
64 {
65 get => this.Fields[(int)XmlConfigSymbolFields.ElementId].AsString();
66 set => this.Set((int)XmlConfigSymbolFields.ElementId, value);
67 }
68
69 public string ElementPath
70 {
71 get => this.Fields[(int)XmlConfigSymbolFields.ElementPath].AsString();
72 set => this.Set((int)XmlConfigSymbolFields.ElementPath, value);
73 }
74
75 public string VerifyPath
76 {
77 get => this.Fields[(int)XmlConfigSymbolFields.VerifyPath].AsString();
78 set => this.Set((int)XmlConfigSymbolFields.VerifyPath, value);
79 }
80
81 public string Name
82 {
83 get => this.Fields[(int)XmlConfigSymbolFields.Name].AsString();
84 set => this.Set((int)XmlConfigSymbolFields.Name, value);
85 }
86
87 public string Value
88 {
89 get => this.Fields[(int)XmlConfigSymbolFields.Value].AsString();
90 set => this.Set((int)XmlConfigSymbolFields.Value, value);
91 }
92
93 public int Flags
94 {
95 get => this.Fields[(int)XmlConfigSymbolFields.Flags].AsNumber();
96 set => this.Set((int)XmlConfigSymbolFields.Flags, value);
97 }
98
99 public string ComponentRef
100 {
101 get => this.Fields[(int)XmlConfigSymbolFields.ComponentRef].AsString();
102 set => this.Set((int)XmlConfigSymbolFields.ComponentRef, value);
103 }
104
105 public int? Sequence
106 {
107 get => this.Fields[(int)XmlConfigSymbolFields.Sequence].AsNullableNumber();
108 set => this.Set((int)XmlConfigSymbolFields.Sequence, value);
109 }
110 }
111} \ No newline at end of file
diff --git a/src/ext/Util/wixext/Symbols/XmlFileSymbol.cs b/src/ext/Util/wixext/Symbols/XmlFileSymbol.cs
new file mode 100644
index 00000000..7d5d991b
--- /dev/null
+++ b/src/ext/Util/wixext/Symbols/XmlFileSymbol.cs
@@ -0,0 +1,95 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Util
4{
5 using WixToolset.Data;
6 using WixToolset.Util.Symbols;
7
8 public static partial class UtilSymbolDefinitions
9 {
10 public static readonly IntermediateSymbolDefinition XmlFile = new IntermediateSymbolDefinition(
11 UtilSymbolDefinitionType.XmlFile.ToString(),
12 new[]
13 {
14 new IntermediateFieldDefinition(nameof(XmlFileSymbolFields.File), IntermediateFieldType.String),
15 new IntermediateFieldDefinition(nameof(XmlFileSymbolFields.ElementPath), IntermediateFieldType.String),
16 new IntermediateFieldDefinition(nameof(XmlFileSymbolFields.Name), IntermediateFieldType.String),
17 new IntermediateFieldDefinition(nameof(XmlFileSymbolFields.Value), IntermediateFieldType.String),
18 new IntermediateFieldDefinition(nameof(XmlFileSymbolFields.Flags), IntermediateFieldType.Number),
19 new IntermediateFieldDefinition(nameof(XmlFileSymbolFields.ComponentRef), IntermediateFieldType.String),
20 new IntermediateFieldDefinition(nameof(XmlFileSymbolFields.Sequence), IntermediateFieldType.Number),
21 },
22 typeof(XmlFileSymbol));
23 }
24}
25
26namespace WixToolset.Util.Symbols
27{
28 using WixToolset.Data;
29
30 public enum XmlFileSymbolFields
31 {
32 File,
33 ElementPath,
34 Name,
35 Value,
36 Flags,
37 ComponentRef,
38 Sequence,
39 }
40
41 public class XmlFileSymbol : IntermediateSymbol
42 {
43 public XmlFileSymbol() : base(UtilSymbolDefinitions.XmlFile, null, null)
44 {
45 }
46
47 public XmlFileSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(UtilSymbolDefinitions.XmlFile, sourceLineNumber, id)
48 {
49 }
50
51 public IntermediateField this[XmlFileSymbolFields index] => this.Fields[(int)index];
52
53 public string File
54 {
55 get => this.Fields[(int)XmlFileSymbolFields.File].AsString();
56 set => this.Set((int)XmlFileSymbolFields.File, value);
57 }
58
59 public string ElementPath
60 {
61 get => this.Fields[(int)XmlFileSymbolFields.ElementPath].AsString();
62 set => this.Set((int)XmlFileSymbolFields.ElementPath, value);
63 }
64
65 public string Name
66 {
67 get => this.Fields[(int)XmlFileSymbolFields.Name].AsString();
68 set => this.Set((int)XmlFileSymbolFields.Name, value);
69 }
70
71 public string Value
72 {
73 get => this.Fields[(int)XmlFileSymbolFields.Value].AsString();
74 set => this.Set((int)XmlFileSymbolFields.Value, value);
75 }
76
77 public int Flags
78 {
79 get => this.Fields[(int)XmlFileSymbolFields.Flags].AsNumber();
80 set => this.Set((int)XmlFileSymbolFields.Flags, value);
81 }
82
83 public string ComponentRef
84 {
85 get => this.Fields[(int)XmlFileSymbolFields.ComponentRef].AsString();
86 set => this.Set((int)XmlFileSymbolFields.ComponentRef, value);
87 }
88
89 public int? Sequence
90 {
91 get => this.Fields[(int)XmlFileSymbolFields.Sequence].AsNullableNumber();
92 set => this.Set((int)XmlFileSymbolFields.Sequence, value);
93 }
94 }
95} \ No newline at end of file
diff --git a/src/ext/Util/wixext/UtilCompiler.cs b/src/ext/Util/wixext/UtilCompiler.cs
new file mode 100644
index 00000000..45079150
--- /dev/null
+++ b/src/ext/Util/wixext/UtilCompiler.cs
@@ -0,0 +1,3889 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Util
4{
5 using System;
6 using System.Collections;
7 using System.Collections.Generic;
8 using System.Globalization;
9 using System.Linq;
10 using System.Text;
11 using System.Text.RegularExpressions;
12 using System.Xml.Linq;
13 using WixToolset.Data;
14 using WixToolset.Data.Symbols;
15 using WixToolset.Extensibility;
16 using WixToolset.Extensibility.Data;
17 using WixToolset.Util.Symbols;
18
19 /// <summary>
20 /// The compiler for the WiX Toolset Utility Extension.
21 /// </summary>
22 public sealed class UtilCompiler : BaseCompilerExtension
23 {
24 // user creation attributes definitions (from sca.h)
25 internal const int UserDontExpirePasswrd = 0x00000001;
26 internal const int UserPasswdCantChange = 0x00000002;
27 internal const int UserPasswdChangeReqdOnLogin = 0x00000004;
28 internal const int UserDisableAccount = 0x00000008;
29 internal const int UserFailIfExists = 0x00000010;
30 internal const int UserUpdateIfExists = 0x00000020;
31 internal const int UserLogonAsService = 0x00000040;
32 internal const int UserLogonAsBatchJob = 0x00000080;
33
34 internal const int UserDontRemoveOnUninstall = 0x00000100;
35 internal const int UserDontCreateUser = 0x00000200;
36 internal const int UserNonVital = 0x00000400;
37
38 private static readonly Regex FindPropertyBrackets = new Regex(@"\[(?!\\|\])|(?<!\[\\\]|\[\\|\\\[)\]", RegexOptions.ExplicitCapture | RegexOptions.Compiled);
39
40 public override XNamespace Namespace => "http://wixtoolset.org/schemas/v4/wxs/util";
41
42 /// <summary>
43 /// Types of Internet shortcuts.
44 /// </summary>
45 public enum InternetShortcutType
46 {
47 /// <summary>Create a .lnk file.</summary>
48 Link = 0,
49
50 /// <summary>Create a .url file.</summary>
51 Url,
52 }
53
54 /// <summary>
55 /// Types of permission setting methods.
56 /// </summary>
57 private enum PermissionType
58 {
59 /// <summary>LockPermissions (normal) type permission setting.</summary>
60 LockPermissions,
61
62 /// <summary>FileSharePermissions type permission setting.</summary>
63 FileSharePermissions,
64
65 /// <summary>SecureObjects type permission setting.</summary>
66 SecureObjects,
67 }
68
69 /// <summary>
70 /// Processes an element for the Compiler.
71 /// </summary>
72 /// <param name="parentElement">Parent element of element to process.</param>
73 /// <param name="element">Element to process.</param>
74 /// <param name="context">Extra information about the context in which this element is being parsed.</param>
75 public override void ParseElement(Intermediate intermediate, IntermediateSection section, XElement parentElement, XElement element, IDictionary<string, string> context)
76 {
77 this.ParsePossibleKeyPathElement(intermediate, section, parentElement, element, context);
78 }
79
80 /// <summary>
81 /// Processes an element for the Compiler.
82 /// </summary>
83 /// <param name="sourceLineNumbers">Source line number for the parent element.</param>
84 /// <param name="parentElement">Parent element of element to process.</param>
85 /// <param name="element">Element to process.</param>
86 /// <param name="contextValues">Extra information about the context in which this element is being parsed.</param>
87 public override IComponentKeyPath ParsePossibleKeyPathElement(Intermediate intermediate, IntermediateSection section, XElement parentElement, XElement element, IDictionary<string, string> context)
88 {
89 IComponentKeyPath possibleKeyPath = null;
90
91 switch (parentElement.Name.LocalName)
92 {
93 case "CreateFolder":
94 var createFolderId = context["DirectoryId"];
95 var createFolderComponentId = context["ComponentId"];
96
97 // If this doesn't parse successfully, something really odd is going on, so let the exception get thrown
98 var createFolderWin64 = Boolean.Parse(context["Win64"]);
99
100 switch (element.Name.LocalName)
101 {
102 case "PermissionEx":
103 this.ParsePermissionExElement(intermediate, section, element, createFolderId, createFolderComponentId, createFolderWin64, "CreateFolder");
104 break;
105 default:
106 this.ParseHelper.UnexpectedElement(parentElement, element);
107 break;
108 }
109 break;
110 case "Component":
111 var componentId = context["ComponentId"];
112 var directoryId = context["DirectoryId"];
113 var componentWin64 = Boolean.Parse(context["Win64"]);
114
115 switch (element.Name.LocalName)
116 {
117 case "EventSource":
118 possibleKeyPath = this.ParseEventSourceElement(intermediate, section, element, componentId);
119 break;
120 case "FileShare":
121 this.ParseFileShareElement(intermediate, section, element, componentId, directoryId);
122 break;
123 case "InternetShortcut":
124 this.ParseInternetShortcutElement(intermediate, section, element, componentId, directoryId);
125 break;
126 case "PerformanceCategory":
127 this.ParsePerformanceCategoryElement(intermediate, section, element, componentId);
128 break;
129 case "RemoveFolderEx":
130 this.ParseRemoveFolderExElement(intermediate, section, element, componentId);
131 break;
132 case "RemoveRegistryKey":
133 this.ParseRemoveRegistryKeyExElement(intermediate, section, element, componentId);
134 break;
135 case "RestartResource":
136 this.ParseRestartResourceElement(intermediate, section, element, componentId);
137 break;
138 case "ServiceConfig":
139 this.ParseServiceConfigElement(intermediate, section, element, componentId, "Component", null);
140 break;
141 case "TouchFile":
142 this.ParseTouchFileElement(intermediate, section, element, componentId, componentWin64);
143 break;
144 case "User":
145 this.ParseUserElement(intermediate, section, element, componentId);
146 break;
147 case "XmlFile":
148 this.ParseXmlFileElement(intermediate, section, element, componentId);
149 break;
150 case "XmlConfig":
151 this.ParseXmlConfigElement(intermediate, section, element, componentId, false);
152 break;
153 default:
154 this.ParseHelper.UnexpectedElement(parentElement, element);
155 break;
156 }
157 break;
158 case "File":
159 var fileId = context["FileId"];
160 var fileComponentId = context["ComponentId"];
161
162 // If this doesn't parse successfully, something really odd is going on, so let the exception get thrown
163 var fileWin64 = Boolean.Parse(context["Win64"]);
164
165 switch (element.Name.LocalName)
166 {
167 case "PerfCounter":
168 this.ParsePerfCounterElement(intermediate, section, element, fileComponentId, fileId);
169 break;
170 case "PermissionEx":
171 this.ParsePermissionExElement(intermediate, section, element, fileId, fileComponentId, fileWin64, "File");
172 break;
173 case "PerfCounterManifest":
174 this.ParsePerfCounterManifestElement(intermediate, section, element, fileComponentId, fileId);
175 break;
176 case "EventManifest":
177 this.ParseEventManifestElement(intermediate, section, element, fileComponentId, fileId);
178 break;
179 case "FormatFile":
180 this.ParseFormatFileElement(intermediate, section, element, fileId, fileWin64);
181 break;
182 default:
183 this.ParseHelper.UnexpectedElement(parentElement, element);
184 break;
185 }
186 break;
187 case "Bundle":
188 case "Fragment":
189 case "Module":
190 case "Package":
191 switch (element.Name.LocalName)
192 {
193 case "CloseApplication":
194 this.ParseCloseApplicationElement(intermediate, section, element);
195 break;
196 case "Group":
197 this.ParseGroupElement(intermediate, section, element, null);
198 break;
199 case "RestartResource":
200 // Currently not supported for Bundles.
201 if (parentElement.Name.LocalName != "Bundle")
202 {
203 this.ParseRestartResourceElement(intermediate, section, element, null);
204 }
205 else
206 {
207 this.ParseHelper.UnexpectedElement(parentElement, element);
208 }
209 break;
210 case "User":
211 this.ParseUserElement(intermediate, section, element, null);
212 break;
213 case "BroadcastEnvironmentChange":
214 case "BroadcastSettingChange":
215 case "CheckRebootRequired":
216 case "ExitEarlyWithSuccess":
217 case "FailWhenDeferred":
218 case "QueryWindowsDirectories":
219 case "QueryWindowsDriverInfo":
220 case "QueryWindowsSuiteInfo":
221 case "QueryWindowsWellKnownSIDs":
222 case "WaitForEvent":
223 case "WaitForEventDeferred":
224 this.AddCustomActionReference(intermediate, section, element, parentElement);
225 break;
226 case "ComponentSearch":
227 case "ComponentSearchRef":
228 case "DirectorySearch":
229 case "DirectorySearchRef":
230 case "FileSearch":
231 case "FileSearchRef":
232 case "ProductSearch":
233 case "ProductSearchRef":
234 case "RegistrySearch":
235 case "RegistrySearchRef":
236 case "WindowsFeatureSearch":
237 case "WindowsFeatureSearchRef":
238 // These will eventually be supported under Module/Product, but are not yet.
239 if (parentElement.Name.LocalName == "Bundle" || parentElement.Name.LocalName == "Fragment")
240 {
241 // TODO: When these are supported by all section types, move
242 // these out of the nested switch and back into the surrounding one.
243 switch (element.Name.LocalName)
244 {
245 case "ComponentSearch":
246 this.ParseComponentSearchElement(intermediate, section, element);
247 break;
248 case "ComponentSearchRef":
249 this.ParseComponentSearchRefElement(intermediate, section, element);
250 break;
251 case "DirectorySearch":
252 this.ParseDirectorySearchElement(intermediate, section, element);
253 break;
254 case "DirectorySearchRef":
255 this.ParseWixSearchRefElement(intermediate, section, element);
256 break;
257 case "FileSearch":
258 this.ParseFileSearchElement(intermediate, section, element);
259 break;
260 case "FileSearchRef":
261 this.ParseWixSearchRefElement(intermediate, section, element);
262 break;
263 case "ProductSearch":
264 this.ParseProductSearchElement(intermediate, section, element);
265 break;
266 case "ProductSearchRef":
267 this.ParseWixSearchRefElement(intermediate, section, element);
268 break;
269 case "RegistrySearch":
270 this.ParseRegistrySearchElement(intermediate, section, element);
271 break;
272 case "RegistrySearchRef":
273 this.ParseWixSearchRefElement(intermediate, section, element);
274 break;
275 case "WindowsFeatureSearch":
276 this.ParseWindowsFeatureSearchElement(intermediate, section, element);
277 break;
278 case "WindowsFeatureSearchRef":
279 this.ParseWindowsFeatureSearchRefElement(intermediate, section, element);
280 break;
281 }
282 }
283 else
284 {
285 this.ParseHelper.UnexpectedElement(parentElement, element);
286 }
287 break;
288 default:
289 this.ParseHelper.UnexpectedElement(parentElement, element);
290 break;
291 }
292 break;
293 case "Registry":
294 case "RegistryKey":
295 case "RegistryValue":
296 var registryId = context["RegistryId"];
297 var registryComponentId = context["ComponentId"];
298
299 // If this doesn't parse successfully, something really odd is going on, so let the exception get thrown
300 var registryWin64 = Boolean.Parse(context["Win64"]);
301
302 switch (element.Name.LocalName)
303 {
304 case "PermissionEx":
305 this.ParsePermissionExElement(intermediate, section, element, registryId, registryComponentId, registryWin64, "Registry");
306 break;
307 default:
308 this.ParseHelper.UnexpectedElement(parentElement, element);
309 break;
310 }
311 break;
312 case "ServiceInstall":
313 var serviceInstallId = context["ServiceInstallId"];
314 var serviceInstallName = context["ServiceInstallName"];
315 var serviceInstallComponentId = context["ServiceInstallComponentId"];
316
317 // If this doesn't parse successfully, something really odd is going on, so let the exception get thrown
318 var serviceInstallWin64 = Boolean.Parse(context["Win64"]);
319
320 switch (element.Name.LocalName)
321 {
322 case "PermissionEx":
323 this.ParsePermissionExElement(intermediate, section, element, serviceInstallId, serviceInstallComponentId, serviceInstallWin64, "ServiceInstall");
324 break;
325 case "ServiceConfig":
326 this.ParseServiceConfigElement(intermediate, section, element, serviceInstallComponentId, "ServiceInstall", serviceInstallName);
327 break;
328 default:
329 this.ParseHelper.UnexpectedElement(parentElement, element);
330 break;
331 }
332 break;
333 case "UI":
334 switch (element.Name.LocalName)
335 {
336 case "BroadcastEnvironmentChange":
337 case "BroadcastSettingChange":
338 case "CheckRebootRequired":
339 case "ExitEarlyWithSuccess":
340 case "FailWhenDeferred":
341 case "QueryWindowsDirectories":
342 case "QueryWindowsDriverInfo":
343 case "QueryWindowsSuiteInfo":
344 case "QueryWindowsWellKnownSIDs":
345 case "WaitForEvent":
346 case "WaitForEventDeferred":
347 this.AddCustomActionReference(intermediate, section, element, parentElement);
348 break;
349 }
350 break;
351 default:
352 this.ParseHelper.UnexpectedElement(parentElement, element);
353 break;
354 }
355
356 return possibleKeyPath;
357 }
358
359 private void AddCustomActionReference(Intermediate intermediate, IntermediateSection section, XElement element, XElement parentElement)
360 {
361 // These elements are not supported for bundles.
362 if (parentElement.Name.LocalName == "Bundle")
363 {
364 this.ParseHelper.UnexpectedElement(parentElement, element);
365 return;
366 }
367
368 var customAction = element.Name.LocalName;
369 switch (element.Name.LocalName)
370 {
371 case "BroadcastEnvironmentChange":
372 case "BroadcastSettingChange":
373 case "CheckRebootRequired":
374 case "ExitEarlyWithSuccess":
375 case "FailWhenDeferred":
376 case "WaitForEvent":
377 case "WaitForEventDeferred":
378 //default: customAction = element.Name.LocalName;
379 break;
380 case "QueryWindowsDirectories":
381 customAction = "QueryOsDirs";
382 break;
383 case "QueryWindowsDriverInfo":
384 customAction = "QueryOsDriverInfo";
385 break;
386 case "QueryWindowsSuiteInfo":
387 customAction = "QueryOsInfo";
388 break;
389 case "QueryWindowsWellKnownSIDs":
390 customAction = "QueryOsWellKnownSID";
391 break;
392 }
393
394 foreach (var attrib in element.Attributes())
395 {
396 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
397 {
398 // no attributes today
399 }
400 else
401 {
402 this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib);
403 }
404 }
405
406 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element);
407
408 this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element);
409
410 this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4" + customAction, this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64);
411 }
412
413 /// <summary>
414 /// Parses the common search attributes shared across all searches.
415 /// </summary>
416 /// <param name="sourceLineNumbers">Source line number for the parent element.</param>
417 /// <param name="attrib">Attribute to parse.</param>
418 /// <param name="id">Value of the Id attribute.</param>
419 /// <param name="variable">Value of the Variable attribute.</param>
420 /// <param name="condition">Value of the Condition attribute.</param>
421 /// <param name="after">Value of the After attribute.</param>
422 private void ParseCommonSearchAttributes(SourceLineNumber sourceLineNumbers, XAttribute attrib, ref Identifier id, ref string variable, ref string condition, ref string after)
423 {
424 switch (attrib.Name.LocalName)
425 {
426 case "Id":
427 id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib);
428 break;
429 case "Variable":
430 variable = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
431 // TODO: handle standard bundle variables
432 break;
433 case "Condition":
434 condition = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
435 break;
436 case "After":
437 after = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
438 break;
439 default:
440 System.Diagnostics.Debug.Assert(false);
441 break;
442 }
443 }
444
445 /// <summary>
446 /// Parses a ComponentSearch element.
447 /// </summary>
448 /// <param name="element">Element to parse.</param>
449 private void ParseComponentSearchElement(Intermediate intermediate, IntermediateSection section, XElement element)
450 {
451 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element);
452 Identifier id = null;
453 string variable = null;
454 string condition = null;
455 string after = null;
456 string guid = null;
457 string productCode = null;
458 var attributes = WixComponentSearchAttributes.KeyPath;
459
460 foreach (var attrib in element.Attributes())
461 {
462 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
463 {
464 switch (attrib.Name.LocalName)
465 {
466 case "Id":
467 case "Variable":
468 case "Condition":
469 case "After":
470 this.ParseCommonSearchAttributes(sourceLineNumbers, attrib, ref id, ref variable, ref condition, ref after);
471 break;
472 case "Guid":
473 guid = this.ParseHelper.GetAttributeGuidValue(sourceLineNumbers, attrib);
474 break;
475 case "ProductCode":
476 productCode = this.ParseHelper.GetAttributeGuidValue(sourceLineNumbers, attrib);
477 break;
478 case "Result":
479 var result = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
480 switch (result)
481 {
482 case "directory":
483 attributes = WixComponentSearchAttributes.WantDirectory;
484 break;
485 case "keyPath":
486 attributes = WixComponentSearchAttributes.KeyPath;
487 break;
488 case "state":
489 attributes = WixComponentSearchAttributes.State;
490 break;
491 default:
492 this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, attrib.Parent.Name.LocalName, attrib.Name.LocalName, result, "directory", "keyPath", "state"));
493 break;
494 }
495 break;
496 default:
497 this.ParseHelper.UnexpectedAttribute(element, attrib);
498 break;
499 }
500 }
501 else
502 {
503 this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib);
504 }
505 }
506
507 if (null == guid)
508 {
509 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Guid"));
510 }
511
512 if (null == id)
513 {
514 id = this.ParseHelper.CreateIdentifier("wcs", variable, condition, after, guid, productCode, attributes.ToString());
515 }
516
517 this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element);
518
519 this.ParseHelper.CreateWixSearchSymbol(section, sourceLineNumbers, element.Name.LocalName, id, variable, condition, after, null);
520
521 if (!this.Messaging.EncounteredError)
522 {
523 section.AddSymbol(new WixComponentSearchSymbol(sourceLineNumbers, id)
524 {
525 Guid = guid,
526 ProductCode = productCode,
527 Attributes = attributes,
528 });
529 }
530 }
531
532 /// <summary>
533 /// Parses a ComponentSearchRef element
534 /// </summary>
535 /// <param name="element">Element to parse.</param>
536 private void ParseComponentSearchRefElement(Intermediate intermediate, IntermediateSection section, XElement element)
537 {
538 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element);
539 string refId = null;
540
541 foreach (var attrib in element.Attributes())
542 {
543 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
544 {
545 switch (attrib.Name.LocalName)
546 {
547 case "Id":
548 refId = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
549 this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, SymbolDefinitions.WixComponentSearch, refId);
550 break;
551 default:
552 this.ParseHelper.UnexpectedAttribute(element, attrib);
553 break;
554 }
555 }
556 else
557 {
558 this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib);
559 }
560 }
561
562 this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element);
563 }
564
565 /// <summary>
566 /// Parses a WindowsFeatureSearch element.
567 /// </summary>
568 /// <param name="element">Element to parse.</param>
569 private void ParseWindowsFeatureSearchElement(Intermediate intermediate, IntermediateSection section, XElement element)
570 {
571 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element);
572 Identifier id = null;
573 string variable = null;
574 string condition = null;
575 string after = null;
576 string feature = null;
577
578 foreach (var attrib in element.Attributes())
579 {
580 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
581 {
582 switch (attrib.Name.LocalName)
583 {
584 case "Id":
585 case "Variable":
586 case "Condition":
587 case "After":
588 this.ParseCommonSearchAttributes(sourceLineNumbers, attrib, ref id, ref variable, ref condition, ref after);
589 break;
590 case "Feature":
591 feature = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
592 switch (feature)
593 {
594 case "sha2CodeSigning":
595 break;
596 default:
597 this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, "Feature", feature, "sha2CodeSigning"));
598 break;
599 }
600 break;
601 default:
602 this.ParseHelper.UnexpectedAttribute(element, attrib);
603 break;
604 }
605 }
606 else
607 {
608 this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib);
609 }
610 }
611
612 if (id == null)
613 {
614 id = this.ParseHelper.CreateIdentifier("wwfs", variable, condition, after);
615 }
616
617 if (feature == null)
618 {
619 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Feature"));
620 }
621
622 this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element);
623
624 var bundleExtensionId = this.ParseHelper.CreateIdentifierValueFromPlatform("Wix4UtilBundleExtension", this.Context.Platform, BurnPlatforms.X86 | BurnPlatforms.X64 | BurnPlatforms.ARM64);
625 if (bundleExtensionId == null)
626 {
627 this.Messaging.Write(ErrorMessages.UnsupportedPlatformForElement(sourceLineNumbers, this.Context.Platform.ToString(), element.Name.LocalName));
628 }
629
630 this.ParseHelper.CreateWixSearchSymbol(section, sourceLineNumbers, element.Name.LocalName, id, variable, condition, after, bundleExtensionId);
631
632 if (!this.Messaging.EncounteredError)
633 {
634 section.AddSymbol(new WixWindowsFeatureSearchSymbol(sourceLineNumbers, id)
635 {
636 Type = feature,
637 });
638 }
639 }
640
641 /// <summary>
642 /// Parses a WindowsFeatureSearchRef element
643 /// </summary>
644 /// <param name="element">Element to parse.</param>
645 private void ParseWindowsFeatureSearchRefElement(Intermediate intermediate, IntermediateSection section, XElement element)
646 {
647 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element);
648
649 foreach (var attrib in element.Attributes())
650 {
651 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
652 {
653 switch (attrib.Name.LocalName)
654 {
655 case "Id":
656 var refId = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
657 this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, UtilSymbolDefinitions.WixWindowsFeatureSearch, refId);
658 break;
659 default:
660 this.ParseHelper.UnexpectedAttribute(element, attrib);
661 break;
662 }
663 }
664 else
665 {
666 this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib);
667 }
668 }
669
670 this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element);
671 }
672
673 /// <summary>
674 /// Parses an event source element.
675 /// </summary>
676 /// <param name="element">Element to parse.</param>
677 /// <param name="componentId">Identifier of parent component.</param>
678 private IComponentKeyPath ParseEventSourceElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId)
679 {
680 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element);
681 string sourceName = null;
682 string logName = null;
683 string categoryMessageFile = null;
684 var categoryCount = CompilerConstants.IntegerNotSet;
685 string eventMessageFile = null;
686 string parameterMessageFile = null;
687 int typesSupported = 0;
688 var isKeyPath = false;
689
690 foreach (var attrib in element.Attributes())
691 {
692 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
693 {
694 switch (attrib.Name.LocalName)
695 {
696 case "CategoryCount":
697 categoryCount = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int32.MaxValue);
698 break;
699 case "CategoryMessageFile":
700 categoryMessageFile = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
701 break;
702 case "EventMessageFile":
703 eventMessageFile = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
704 break;
705 case "KeyPath":
706 isKeyPath = YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib);
707 break;
708 case "Log":
709 logName = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
710 if ("Security" == logName)
711 {
712 this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName, logName, "Application", "System", "<customEventLog>"));
713 }
714 break;
715 case "Name":
716 sourceName = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
717 break;
718 case "ParameterMessageFile":
719 parameterMessageFile = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
720 break;
721 case "SupportsErrors":
722 if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib))
723 {
724 typesSupported |= 0x01; // EVENTLOG_ERROR_TYPE
725 }
726 break;
727 case "SupportsFailureAudits":
728 if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib))
729 {
730 typesSupported |= 0x10; // EVENTLOG_AUDIT_FAILURE
731 }
732 break;
733 case "SupportsInformationals":
734 if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib))
735 {
736 typesSupported |= 0x04; // EVENTLOG_INFORMATION_TYPE
737 }
738 break;
739 case "SupportsSuccessAudits":
740 if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib))
741 {
742 typesSupported |= 0x08; // EVENTLOG_AUDIT_SUCCESS
743 }
744 break;
745 case "SupportsWarnings":
746 if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib))
747 {
748 typesSupported |= 0x02; // EVENTLOG_WARNING_TYPE
749 }
750 break;
751 default:
752 this.ParseHelper.UnexpectedAttribute(element, attrib);
753 break;
754 }
755 }
756 else
757 {
758 this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib);
759 }
760 }
761
762 if (null == sourceName)
763 {
764 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Name"));
765 }
766
767 if (null == logName)
768 {
769 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "EventLog"));
770 }
771
772 if (null == eventMessageFile)
773 {
774 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "EventMessageFile"));
775 }
776
777 if (null == categoryMessageFile && 0 < categoryCount)
778 {
779 this.Messaging.Write(ErrorMessages.IllegalAttributeWithoutOtherAttributes(sourceLineNumbers, element.Name.LocalName, "CategoryCount", "CategoryMessageFile"));
780 }
781
782 if (null != categoryMessageFile && CompilerConstants.IntegerNotSet == categoryCount)
783 {
784 this.Messaging.Write(ErrorMessages.IllegalAttributeWithoutOtherAttributes(sourceLineNumbers, element.Name.LocalName, "CategoryMessageFile", "CategoryCount"));
785 }
786
787 this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element);
788
789 string eventSourceKey = $@"SYSTEM\CurrentControlSet\Services\EventLog\{logName}\{sourceName}";
790 var id = this.ParseHelper.CreateRegistrySymbol(section, sourceLineNumbers, RegistryRootType.LocalMachine, eventSourceKey, "EventMessageFile", String.Concat("#%", eventMessageFile), componentId, false);
791
792 if (null != categoryMessageFile)
793 {
794 this.ParseHelper.CreateRegistrySymbol(section, sourceLineNumbers, RegistryRootType.LocalMachine, eventSourceKey, "CategoryMessageFile", String.Concat("#%", categoryMessageFile), componentId, false);
795 }
796
797 if (CompilerConstants.IntegerNotSet != categoryCount)
798 {
799 this.ParseHelper.CreateRegistrySymbol(section, sourceLineNumbers, RegistryRootType.LocalMachine, eventSourceKey, "CategoryCount", String.Concat("#", categoryCount), componentId, false);
800 }
801
802 if (null != parameterMessageFile)
803 {
804 this.ParseHelper.CreateRegistrySymbol(section, sourceLineNumbers, RegistryRootType.LocalMachine, eventSourceKey, "ParameterMessageFile", String.Concat("#%", parameterMessageFile), componentId, false);
805 }
806
807 if (0 != typesSupported)
808 {
809 this.ParseHelper.CreateRegistrySymbol(section, sourceLineNumbers, RegistryRootType.LocalMachine, eventSourceKey, "TypesSupported", String.Concat("#", typesSupported), componentId, false);
810 }
811
812 var componentKeyPath = this.CreateComponentKeyPath();
813 componentKeyPath.Id = id.Id;
814 componentKeyPath.Explicit = isKeyPath;
815 componentKeyPath.Type = PossibleKeyPathType.Registry;
816 return componentKeyPath;
817 }
818
819 /// <summary>
820 /// Parses a close application element.
821 /// </summary>
822 /// <param name="element">Element to parse.</param>
823 private void ParseCloseApplicationElement(Intermediate intermediate, IntermediateSection section, XElement element)
824 {
825 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element);
826 string condition = null;
827 string description = null;
828 string target = null;
829 string property = null;
830 Identifier id = null;
831 int attributes = 2; // default to CLOSEAPP_ATTRIBUTE_REBOOTPROMPT enabled
832 var sequence = CompilerConstants.IntegerNotSet;
833 var terminateExitCode = CompilerConstants.IntegerNotSet;
834 var timeout = CompilerConstants.IntegerNotSet;
835
836 foreach (var attrib in element.Attributes())
837 {
838 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
839 {
840 switch (attrib.Name.LocalName)
841 {
842 case "Id":
843 id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib);
844 break;
845 case "Condition":
846 condition = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
847 break;
848 case "Description":
849 description = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
850 break;
851 case "Property":
852 property = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
853 break;
854 case "Sequence":
855 sequence = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int32.MaxValue);
856 break;
857 case "Timeout":
858 timeout = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int32.MaxValue);
859 break;
860 case "Target":
861 target = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
862 break;
863 case "CloseMessage":
864 if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib))
865 {
866 attributes |= 1; // CLOSEAPP_ATTRIBUTE_CLOSEMESSAGE
867 }
868 else
869 {
870 attributes &= ~1; // CLOSEAPP_ATTRIBUTE_CLOSEMESSAGE
871 }
872 break;
873 case "EndSessionMessage":
874 if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib))
875 {
876 attributes |= 8; // CLOSEAPP_ATTRIBUTE_ENDSESSIONMESSAGE
877 }
878 else
879 {
880 attributes &= ~8; // CLOSEAPP_ATTRIBUTE_ENDSESSIONMESSAGE
881 }
882 break;
883 case "PromptToContinue":
884 if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib))
885 {
886 attributes |= 0x40; // CLOSEAPP_ATTRIBUTE_PROMPTTOCONTINUE
887 }
888 else
889 {
890 attributes &= ~0x40; // CLOSEAPP_ATTRIBUTE_PROMPTTOCONTINUE
891 }
892 break;
893 case "RebootPrompt":
894 if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib))
895 {
896 attributes |= 2; // CLOSEAPP_ATTRIBUTE_REBOOTPROMPT
897 }
898 else
899 {
900 attributes &= ~2; // CLOSEAPP_ATTRIBUTE_REBOOTPROMPT
901 }
902 break;
903 case "ElevatedCloseMessage":
904 if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib))
905 {
906 attributes |= 4; // CLOSEAPP_ATTRIBUTE_ELEVATEDCLOSEMESSAGE
907 }
908 else
909 {
910 attributes &= ~4; // CLOSEAPP_ATTRIBUTE_ELEVATEDCLOSEMESSAGE
911 }
912 break;
913 case "ElevatedEndSessionMessage":
914 if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib))
915 {
916 attributes |= 0x10; // CLOSEAPP_ATTRIBUTE_ELEVATEDENDSESSIONMESSAGE
917 }
918 else
919 {
920 attributes &= ~0x10; // CLOSEAPP_ATTRIBUTE_ELEVATEDENDSESSIONMESSAGE
921 }
922 break;
923 case "TerminateProcess":
924 terminateExitCode = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int32.MaxValue);
925 attributes |= 0x20; // CLOSEAPP_ATTRIBUTE_TERMINATEPROCESS
926 break;
927 default:
928 this.ParseHelper.UnexpectedAttribute(element, attrib);
929 break;
930 }
931 }
932 else
933 {
934 this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib);
935 }
936 }
937
938 if (null == target)
939 {
940 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Target"));
941 }
942 else if (null == id)
943 {
944 id = this.ParseHelper.CreateIdentifier("ca", target);
945 }
946
947 if (String.IsNullOrEmpty(description) && 0x40 == (attributes & 0x40))
948 {
949 this.Messaging.Write(ErrorMessages.IllegalAttributeValueWithoutOtherAttribute(sourceLineNumbers, element.Name.LocalName, "PromptToContinue", "yes", "Description"));
950 }
951
952 if (0x22 == (attributes & 0x22))
953 {
954 this.Messaging.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, element.Name.LocalName, "TerminateProcess", "RebootPrompt", "yes"));
955 }
956
957 this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element);
958
959 this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4CloseApplications", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64);
960
961 if (!this.Messaging.EncounteredError)
962 {
963 var symbol = section.AddSymbol(new WixCloseApplicationSymbol(sourceLineNumbers, id)
964 {
965 Target = target,
966 Description = description,
967 Condition = condition,
968 Attributes = attributes,
969 Property = property,
970 });
971 if (CompilerConstants.IntegerNotSet != sequence)
972 {
973 symbol.Sequence = sequence;
974 }
975 if (CompilerConstants.IntegerNotSet != terminateExitCode)
976 {
977 symbol.TerminateExitCode = terminateExitCode;
978 }
979 if (CompilerConstants.IntegerNotSet != timeout)
980 {
981 symbol.Timeout = timeout * 1000; // make the timeout milliseconds in the table.
982 }
983 }
984 }
985
986 /// <summary>
987 /// Parses a DirectorySearch element.
988 /// </summary>
989 /// <param name="element">Element to parse.</param>
990 private void ParseDirectorySearchElement(Intermediate intermediate, IntermediateSection section, XElement element)
991 {
992 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element);
993 Identifier id = null;
994 string variable = null;
995 string condition = null;
996 string after = null;
997 string path = null;
998 var attributes = WixFileSearchAttributes.IsDirectory;
999
1000 foreach (var attrib in element.Attributes())
1001 {
1002 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
1003 {
1004 switch (attrib.Name.LocalName)
1005 {
1006 case "Id":
1007 case "Variable":
1008 case "Condition":
1009 case "After":
1010 this.ParseCommonSearchAttributes(sourceLineNumbers, attrib, ref id, ref variable, ref condition, ref after);
1011 break;
1012 case "Path":
1013 path = this.ParseHelper.GetAttributeLongFilename(sourceLineNumbers, attrib, false, true);
1014 break;
1015 case "Result":
1016 var result = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
1017 switch (result)
1018 {
1019 case "exists":
1020 attributes |= WixFileSearchAttributes.WantExists;
1021 break;
1022 default:
1023 this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, attrib.Parent.Name.LocalName, attrib.Name.LocalName, result, "exists"));
1024 break;
1025 }
1026 break;
1027 default:
1028 this.ParseHelper.UnexpectedAttribute(element, attrib);
1029 break;
1030 }
1031 }
1032 else
1033 {
1034 this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib);
1035 }
1036 }
1037
1038 if (null == path)
1039 {
1040 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Path"));
1041 }
1042
1043 if (null == id)
1044 {
1045 id = this.ParseHelper.CreateIdentifier("wds", variable, condition, after, path, attributes.ToString());
1046 }
1047
1048 this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element);
1049
1050 this.ParseHelper.CreateWixSearchSymbol(section, sourceLineNumbers, element.Name.LocalName, id, variable, condition, after, null);
1051
1052 if (!this.Messaging.EncounteredError)
1053 {
1054 this.CreateWixFileSearchRow(section, sourceLineNumbers, id, path, attributes);
1055 }
1056 }
1057
1058 /// <summary>
1059 /// Parses a DirectorySearchRef, FileSearchRef, ProductSearchRef, and RegistrySearchRef elements
1060 /// </summary>
1061 /// <param name="node">Element to parse.</param>
1062 private void ParseWixSearchRefElement(Intermediate intermediate, IntermediateSection section, XElement node)
1063 {
1064 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(node);
1065
1066 foreach (XAttribute attrib in node.Attributes())
1067 {
1068 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
1069 {
1070 switch (attrib.Name.LocalName)
1071 {
1072 case "Id":
1073 var refId = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
1074 this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, SymbolDefinitions.WixSearch, refId);
1075 break;
1076 default:
1077 this.ParseHelper.UnexpectedAttribute(node, attrib);
1078 break;
1079 }
1080 }
1081 else
1082 {
1083 this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, node, attrib);
1084 }
1085 }
1086
1087 this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, node);
1088 }
1089
1090 /// <summary>
1091 /// Parses a FileSearch element.
1092 /// </summary>
1093 /// <param name="node">Element to parse.</param>
1094 private void ParseFileSearchElement(Intermediate intermediate, IntermediateSection section, XElement node)
1095 {
1096 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(node);
1097 Identifier id = null;
1098 string variable = null;
1099 string condition = null;
1100 string after = null;
1101 string path = null;
1102 var attributes = WixFileSearchAttributes.Default;
1103
1104 foreach (var attrib in node.Attributes())
1105 {
1106 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
1107 {
1108 switch (attrib.Name.LocalName)
1109 {
1110 case "Id":
1111 case "Variable":
1112 case "Condition":
1113 case "After":
1114 this.ParseCommonSearchAttributes(sourceLineNumbers, attrib, ref id, ref variable, ref condition, ref after);
1115 break;
1116 case "Path":
1117 path = this.ParseHelper.GetAttributeLongFilename(sourceLineNumbers, attrib, false, true);
1118 break;
1119 case "Result":
1120 string result = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
1121 switch (result)
1122 {
1123 case "exists":
1124 attributes |= WixFileSearchAttributes.WantExists;
1125 break;
1126 case "version":
1127 attributes |= WixFileSearchAttributes.WantVersion;
1128 break;
1129 default:
1130 this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, attrib.Parent.Name.LocalName, attrib.Name.LocalName, result, "exists", "version"));
1131 break;
1132 }
1133 break;
1134 default:
1135 this.ParseHelper.UnexpectedAttribute(node, attrib);
1136 break;
1137 }
1138 }
1139 else
1140 {
1141 this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, node, attrib);
1142 }
1143 }
1144
1145 if (null == path)
1146 {
1147 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Path"));
1148 }
1149
1150 if (null == id)
1151 {
1152 id = this.ParseHelper.CreateIdentifier("wfs", variable, condition, after, path, attributes.ToString());
1153 }
1154
1155 this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, node);
1156
1157 this.ParseHelper.CreateWixSearchSymbol(section, sourceLineNumbers, node.Name.LocalName, id, variable, condition, after, null);
1158
1159 if (!this.Messaging.EncounteredError)
1160 {
1161 this.CreateWixFileSearchRow(section, sourceLineNumbers, id, path, attributes);
1162 }
1163 }
1164
1165 /// <summary>
1166 /// Creates a row in the WixFileSearch table.
1167 /// </summary>
1168 /// <param name="sourceLineNumbers">Source line number for the parent element.</param>
1169 /// <param name="id">Identifier of the search (key into the WixSearch table)</param>
1170 /// <param name="path">File/directory path to search for.</param>
1171 /// <param name="attributes"></param>
1172 private void CreateWixFileSearchRow(IntermediateSection section, SourceLineNumber sourceLineNumbers, Identifier id, string path, WixFileSearchAttributes attributes)
1173 {
1174 section.AddSymbol(new WixFileSearchSymbol(sourceLineNumbers, id)
1175 {
1176 Path = path,
1177 Attributes = attributes,
1178 });
1179 }
1180
1181 /// <summary>
1182 /// Parses a file share element.
1183 /// </summary>
1184 /// <param name="element">Element to parse.</param>
1185 /// <param name="componentId">Identifier of parent component.</param>
1186 /// <param name="directoryId">Identifier of referred to directory.</param>
1187 private void ParseFileShareElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId, string directoryId)
1188 {
1189 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element);
1190 string description = null;
1191 string name = null;
1192 Identifier id = null;
1193
1194 foreach (var attrib in element.Attributes())
1195 {
1196 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
1197 {
1198 switch (attrib.Name.LocalName)
1199 {
1200 case "Id":
1201 id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib);
1202 break;
1203 case "Name":
1204 name = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
1205 break;
1206 case "Description":
1207 description = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
1208 break;
1209 default:
1210 this.ParseHelper.UnexpectedAttribute(element, attrib);
1211 break;
1212 }
1213 }
1214 else
1215 {
1216 this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib);
1217 }
1218 }
1219
1220 if (null == id)
1221 {
1222 id = this.ParseHelper.CreateIdentifier("ufs", componentId, name);
1223 }
1224
1225 if (null == name)
1226 {
1227 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Name"));
1228 }
1229
1230 if (!element.Elements().Any())
1231 {
1232 this.Messaging.Write(ErrorMessages.ExpectedElement(sourceLineNumbers, element.Name.LocalName, "FileSharePermission"));
1233 }
1234
1235 foreach (var child in element.Elements())
1236 {
1237 if (this.Namespace == child.Name.Namespace)
1238 {
1239 switch (child.Name.LocalName)
1240 {
1241 case "FileSharePermission":
1242 this.ParseFileSharePermissionElement(intermediate, section, child, id);
1243 break;
1244 default:
1245 this.ParseHelper.UnexpectedElement(element, child);
1246 break;
1247 }
1248 }
1249 else
1250 {
1251 this.ParseHelper.ParseExtensionElement(this.Context.Extensions, intermediate, section, element, child);
1252 }
1253 }
1254
1255 this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4ConfigureSmbInstall", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64);
1256 this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4ConfigureSmbUninstall", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64);
1257
1258 if (!this.Messaging.EncounteredError)
1259 {
1260 section.AddSymbol(new FileShareSymbol(sourceLineNumbers, id)
1261 {
1262 ShareName = name,
1263 ComponentRef = componentId,
1264 Description = description,
1265 DirectoryRef = directoryId,
1266 });
1267 }
1268 }
1269
1270 /// <summary>
1271 /// Parses a FileSharePermission element.
1272 /// </summary>
1273 /// <param name="element">Element to parse.</param>
1274 /// <param name="fileShareId">The identifier of the parent FileShare element.</param>
1275 private void ParseFileSharePermissionElement(Intermediate intermediate, IntermediateSection section, XElement element, Identifier fileShareId)
1276 {
1277 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element);
1278 var bits = new BitArray(32);
1279 string user = null;
1280
1281 foreach (var attrib in element.Attributes())
1282 {
1283 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
1284 {
1285 switch (attrib.Name.LocalName)
1286 {
1287 case "User":
1288 user = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
1289 this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, UtilSymbolDefinitions.User, user);
1290 break;
1291 default:
1292 var attribValue = this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib);
1293 if (!this.TrySetBitFromName(UtilConstants.StandardPermissions, attrib.Name.LocalName, attribValue, bits, 16))
1294 {
1295 if (!this.TrySetBitFromName(UtilConstants.GenericPermissions, attrib.Name.LocalName, attribValue, bits, 28))
1296 {
1297 if (!this.TrySetBitFromName(UtilConstants.FolderPermissions, attrib.Name.LocalName, attribValue, bits, 0))
1298 {
1299 this.ParseHelper.UnexpectedAttribute(element, attrib);
1300 break;
1301 }
1302 }
1303 }
1304 break;
1305 }
1306 }
1307 else
1308 {
1309 this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib);
1310 }
1311 }
1312
1313 var permission = this.CreateIntegerFromBitArray(bits);
1314
1315 if (null == user)
1316 {
1317 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "User"));
1318 }
1319
1320 if (Int32.MinValue == permission) // just GENERIC_READ, which is MSI_NULL
1321 {
1322 this.Messaging.Write(ErrorMessages.GenericReadNotAllowed(sourceLineNumbers));
1323 }
1324
1325 this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element);
1326
1327 if (!this.Messaging.EncounteredError)
1328 {
1329 section.AddSymbol(new FileSharePermissionsSymbol(sourceLineNumbers)
1330 {
1331 FileShareRef = fileShareId.Id,
1332 UserRef = user,
1333 Permissions = permission,
1334 });
1335 }
1336 }
1337
1338 /// <summary>
1339 /// Parses a group element.
1340 /// </summary>
1341 /// <param name="element">Node to be parsed.</param>
1342 /// <param name="componentId">Component Id of the parent component of this element.</param>
1343 private void ParseGroupElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId)
1344 {
1345 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element);
1346 Identifier id = null;
1347 string domain = null;
1348 string name = null;
1349
1350 foreach (var attrib in element.Attributes())
1351 {
1352 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
1353 {
1354 switch (attrib.Name.LocalName)
1355 {
1356 case "Id":
1357 id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib);
1358 break;
1359 case "Name":
1360 name = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
1361 break;
1362 case "Domain":
1363 domain = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
1364 break;
1365 default:
1366 this.ParseHelper.UnexpectedAttribute(element, attrib);
1367 break;
1368 }
1369 }
1370 else
1371 {
1372 this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib);
1373 }
1374 }
1375
1376 if (null == id)
1377 {
1378 id = this.ParseHelper.CreateIdentifier("ugr", componentId, domain, name);
1379 }
1380
1381 this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element);
1382
1383 if (!this.Messaging.EncounteredError)
1384 {
1385 section.AddSymbol(new GroupSymbol(sourceLineNumbers, id)
1386 {
1387 ComponentRef = componentId,
1388 Name = name,
1389 Domain = domain,
1390 });
1391 }
1392 }
1393
1394 /// <summary>
1395 /// Parses a GroupRef element
1396 /// </summary>
1397 /// <param name="element">Element to parse.</param>
1398 /// <param name="userId">Required user id to be joined to the group.</param>
1399 private void ParseGroupRefElement(Intermediate intermediate, IntermediateSection section, XElement element, string userId)
1400 {
1401 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element);
1402 string groupId = null;
1403
1404 foreach (var attrib in element.Attributes())
1405 {
1406 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
1407 {
1408 switch (attrib.Name.LocalName)
1409 {
1410 case "Id":
1411 groupId = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
1412 this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, UtilSymbolDefinitions.Group, groupId);
1413 break;
1414 default:
1415 this.ParseHelper.UnexpectedAttribute(element, attrib);
1416 break;
1417 }
1418 }
1419 else
1420 {
1421 this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib);
1422 }
1423 }
1424
1425 this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element);
1426
1427 if (!this.Messaging.EncounteredError)
1428 {
1429 section.AddSymbol(new UserGroupSymbol(sourceLineNumbers)
1430 {
1431 UserRef = userId,
1432 GroupRef = groupId,
1433 });
1434 }
1435 }
1436
1437 /// <summary>
1438 /// Parses an InternetShortcut element.
1439 /// </summary>
1440 /// <param name="element">Element to parse.</param>
1441 /// <param name="componentId">Identifier of parent component.</param>
1442 /// <param name="defaultTarget">Default directory if none is specified on the InternetShortcut element.</param>
1443 private void ParseInternetShortcutElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId, string defaultTarget)
1444 {
1445 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element);
1446 Identifier id = null;
1447 string name = null;
1448 string target = null;
1449 string directoryId = null;
1450 string type = null;
1451 string iconFile = null;
1452 int iconIndex = 0;
1453
1454 foreach (var attrib in element.Attributes())
1455 {
1456 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
1457 {
1458 switch (attrib.Name.LocalName)
1459 {
1460 case "Directory":
1461 directoryId = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
1462 break;
1463 case "Id":
1464 id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib);
1465 break;
1466 case "Name":
1467 name = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
1468 break;
1469 case "Target":
1470 target = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
1471 break;
1472 case "Type":
1473 type = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
1474 break;
1475 case "IconFile":
1476 iconFile = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
1477 break;
1478 case "IconIndex":
1479 iconIndex = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int32.MaxValue);
1480 break;
1481 default:
1482 this.ParseHelper.UnexpectedAttribute(element, attrib);
1483 break;
1484 }
1485 }
1486 else
1487 {
1488 this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib);
1489 }
1490 }
1491
1492 // If there was no directoryId specified on the InternetShortcut element, default to the one on
1493 // the parent component.
1494 if (null == directoryId)
1495 {
1496 directoryId = defaultTarget;
1497 }
1498
1499 if (null == id)
1500 {
1501 id = this.ParseHelper.CreateIdentifier("uis", componentId, directoryId, name, target);
1502 }
1503
1504 // In theory this can never be the case, since InternetShortcut can only be under
1505 // a component element, and if the Directory wasn't specified the default will come
1506 // from the component. However, better safe than sorry, so here's a check to make sure
1507 // it didn't wind up being null after setting it to the defaultTarget.
1508 if (null == directoryId)
1509 {
1510 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Directory"));
1511 }
1512
1513 if (null == name)
1514 {
1515 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Name"));
1516 }
1517
1518 if (null == target)
1519 {
1520 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Target"));
1521 }
1522
1523 this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element);
1524
1525 var shortcutType = InternetShortcutType.Link;
1526 if (String.Equals(type, "url", StringComparison.OrdinalIgnoreCase))
1527 {
1528 shortcutType = InternetShortcutType.Url;
1529 }
1530
1531 if (!this.Messaging.EncounteredError)
1532 {
1533 this.CreateWixInternetShortcut(section, sourceLineNumbers, componentId, directoryId, id, name, target, shortcutType, iconFile, iconIndex);
1534 }
1535 }
1536
1537 /// <summary>
1538 /// Creates the rows needed for WixInternetShortcut to work.
1539 /// </summary>
1540 /// <param name="core">The CompilerCore object used to create rows.</param>
1541 /// <param name="sourceLineNumbers">Source line information about the owner element.</param>
1542 /// <param name="componentId">Identifier of parent component.</param>
1543 /// <param name="directoryId">Identifier of directory containing shortcut.</param>
1544 /// <param name="id">Identifier of shortcut.</param>
1545 /// <param name="name">Name of shortcut without extension.</param>
1546 /// <param name="target">Target URL of shortcut.</param>
1547 private void CreateWixInternetShortcut(IntermediateSection section, SourceLineNumber sourceLineNumbers, string componentId, string directoryId, Identifier shortcutId, string name, string target, InternetShortcutType type, string iconFile, int iconIndex)
1548 {
1549 // add the appropriate extension based on type of shortcut
1550 name = String.Concat(name, InternetShortcutType.Url == type ? ".url" : ".lnk");
1551
1552 section.AddSymbol(new WixInternetShortcutSymbol(sourceLineNumbers, shortcutId)
1553 {
1554 ComponentRef = componentId,
1555 DirectoryRef = directoryId,
1556 Name = name,
1557 Target = target,
1558 Attributes = (int)type,
1559 IconFile = iconFile,
1560 IconIndex = iconIndex,
1561 });
1562
1563 this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4SchedInternetShortcuts", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64);
1564
1565 // make sure we have a CreateFolder table so that the immediate CA can add temporary rows to handle installation and uninstallation
1566 this.ParseHelper.EnsureTable(section, sourceLineNumbers, "CreateFolder");
1567
1568 // use built-in MSI functionality to remove the shortcuts rather than doing so via CA
1569 section.AddSymbol(new RemoveFileSymbol(sourceLineNumbers, shortcutId)
1570 {
1571 ComponentRef = componentId,
1572 DirPropertyRef = directoryId,
1573 OnUninstall = true,
1574 FileName = name,
1575 });
1576 }
1577
1578 /// <summary>
1579 /// Parses a performance category element.
1580 /// </summary>
1581 /// <param name="element">Element to parse.</param>
1582 /// <param name="componentId">Identifier of parent component.</param>
1583 private void ParsePerformanceCategoryElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId)
1584 {
1585 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element);
1586 Identifier id = null;
1587 string name = null;
1588 string help = null;
1589 var multiInstance = YesNoType.No;
1590 int defaultLanguage = 0x09; // default to "english"
1591
1592 var parsedPerformanceCounters = new List<ParsedPerformanceCounter>();
1593
1594 // default to managed performance counter
1595 var library = "netfxperf.dll";
1596 var openEntryPoint = "OpenPerformanceData";
1597 var collectEntryPoint = "CollectPerformanceData";
1598 var closeEntryPoint = "ClosePerformanceData";
1599
1600 foreach (var attrib in element.Attributes())
1601 {
1602 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
1603 {
1604 switch (attrib.Name.LocalName)
1605 {
1606 case "Close":
1607 closeEntryPoint = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
1608 break;
1609 case "Collect":
1610 collectEntryPoint = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
1611 break;
1612 case "DefaultLanguage":
1613 defaultLanguage = this.GetPerformanceCounterLanguage(sourceLineNumbers, attrib);
1614 break;
1615 case "Help":
1616 help = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
1617 break;
1618 case "Id":
1619 id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib);
1620 break;
1621 case "Library":
1622 library = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
1623 break;
1624 case "MultiInstance":
1625 multiInstance = this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib);
1626 break;
1627 case "Name":
1628 name = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
1629 break;
1630 case "Open":
1631 openEntryPoint = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
1632 break;
1633 default:
1634 this.ParseHelper.UnexpectedAttribute(element, attrib);
1635 break;
1636 }
1637 }
1638 else
1639 {
1640 this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib);
1641 }
1642 }
1643
1644 if (null == id && null == name)
1645 {
1646 this.Messaging.Write(ErrorMessages.ExpectedAttributes(sourceLineNumbers, element.Name.LocalName, "Id", "Name"));
1647 }
1648 else if (null == id)
1649 {
1650 id = this.ParseHelper.CreateIdentifier("upc", componentId, name);
1651 }
1652 else if (null == name)
1653 {
1654 name = id.Id;
1655 }
1656
1657 // Process the child counter elements.
1658 foreach (var child in element.Elements())
1659 {
1660 if (this.Namespace == child.Name.Namespace)
1661 {
1662 switch (child.Name.LocalName)
1663 {
1664 case "PerformanceCounter":
1665 var counter = this.ParsePerformanceCounterElement(intermediate, section, child, defaultLanguage);
1666 if (null != counter)
1667 {
1668 parsedPerformanceCounters.Add(counter);
1669 }
1670 break;
1671 default:
1672 this.ParseHelper.UnexpectedElement(element, child);
1673 break;
1674 }
1675 }
1676 else
1677 {
1678 this.ParseHelper.ParseExtensionElement(this.Context.Extensions, intermediate, section, element, child);
1679 }
1680 }
1681
1682
1683 if (!this.Messaging.EncounteredError)
1684 {
1685 // Calculate the ini and h file content.
1686 var objectName = "OBJECT_1";
1687 var objectLanguage = defaultLanguage.ToString("D3", CultureInfo.InvariantCulture);
1688
1689 var sbIniData = new StringBuilder();
1690 sbIniData.AppendFormat("[info]\r\ndrivername={0}\r\nsymbolfile=wixperf.h\r\n\r\n[objects]\r\n{1}_{2}_NAME=\r\n\r\n[languages]\r\n{2}=LANG{2}\r\n\r\n", name, objectName, objectLanguage);
1691 sbIniData.AppendFormat("[text]\r\n{0}_{1}_NAME={2}\r\n", objectName, objectLanguage, name);
1692 if (null != help)
1693 {
1694 sbIniData.AppendFormat("{0}_{1}_HELP={2}\r\n", objectName, objectLanguage, help);
1695 }
1696
1697 int symbolConstantsCounter = 0;
1698 var sbSymbolicConstants = new StringBuilder();
1699 sbSymbolicConstants.AppendFormat("#define {0} {1}\r\n", objectName, symbolConstantsCounter);
1700
1701 var sbCounterNames = new StringBuilder("[~]");
1702 var sbCounterTypes = new StringBuilder("[~]");
1703 for (int i = 0; i < parsedPerformanceCounters.Count; ++i)
1704 {
1705 var counter = parsedPerformanceCounters[i];
1706 var counterName = String.Concat("DEVICE_COUNTER_", i + 1);
1707
1708 sbIniData.AppendFormat("{0}_{1}_NAME={2}\r\n", counterName, counter.Language, counter.Name);
1709 if (null != counter.Help)
1710 {
1711 sbIniData.AppendFormat("{0}_{1}_HELP={2}\r\n", counterName, counter.Language, counter.Help);
1712 }
1713
1714 symbolConstantsCounter += 2;
1715 sbSymbolicConstants.AppendFormat("#define {0} {1}\r\n", counterName, symbolConstantsCounter);
1716
1717 sbCounterNames.Append(UtilCompiler.FindPropertyBrackets.Replace(counter.Name, this.EscapeProperties));
1718 sbCounterNames.Append("[~]");
1719 sbCounterTypes.Append(counter.Type);
1720 sbCounterTypes.Append("[~]");
1721 }
1722
1723 sbSymbolicConstants.AppendFormat("#define LAST_{0}_COUNTER_OFFSET {1}\r\n", objectName, symbolConstantsCounter);
1724
1725 // Add the calculated INI and H strings to the PerformanceCategory table.
1726 section.AddSymbol(new PerformanceCategorySymbol(sourceLineNumbers, id)
1727 {
1728 ComponentRef = componentId,
1729 Name = name,
1730 IniData = sbIniData.ToString(),
1731 ConstantData = sbSymbolicConstants.ToString(),
1732 });
1733
1734 // Set up the application's performance key.
1735 var escapedName = UtilCompiler.FindPropertyBrackets.Replace(name, this.EscapeProperties);
1736 var linkageKey = String.Format(@"SYSTEM\CurrentControlSet\Services\{0}\Linkage", escapedName);
1737 var performanceKey = String.Format(@"SYSTEM\CurrentControlSet\Services\{0}\Performance", escapedName);
1738
1739 this.ParseHelper.CreateRegistrySymbol(section, sourceLineNumbers, RegistryRootType.LocalMachine, linkageKey, "Export", escapedName, componentId, false);
1740 this.ParseHelper.CreateRegistrySymbol(section, sourceLineNumbers, RegistryRootType.LocalMachine, performanceKey, "-", null, componentId, false);
1741 this.ParseHelper.CreateRegistrySymbol(section, sourceLineNumbers, RegistryRootType.LocalMachine, performanceKey, "Library", library, componentId, false);
1742 this.ParseHelper.CreateRegistrySymbol(section, sourceLineNumbers, RegistryRootType.LocalMachine, performanceKey, "Open", openEntryPoint, componentId, false);
1743 this.ParseHelper.CreateRegistrySymbol(section, sourceLineNumbers, RegistryRootType.LocalMachine, performanceKey, "Collect", collectEntryPoint, componentId, false);
1744 this.ParseHelper.CreateRegistrySymbol(section, sourceLineNumbers, RegistryRootType.LocalMachine, performanceKey, "Close", closeEntryPoint, componentId, false);
1745 this.ParseHelper.CreateRegistrySymbol(section, sourceLineNumbers, RegistryRootType.LocalMachine, performanceKey, "IsMultiInstance", YesNoType.Yes == multiInstance ? "#1" : "#0", componentId, false);
1746 this.ParseHelper.CreateRegistrySymbol(section, sourceLineNumbers, RegistryRootType.LocalMachine, performanceKey, "Counter Names", sbCounterNames.ToString(), componentId, false);
1747 this.ParseHelper.CreateRegistrySymbol(section, sourceLineNumbers, RegistryRootType.LocalMachine, performanceKey, "Counter Types", sbCounterTypes.ToString(), componentId, false);
1748 }
1749
1750 this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4InstallPerfCounterData", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64);
1751 this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4UninstallPerfCounterData", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64);
1752 }
1753
1754 /// <summary>
1755 /// Gets the performance counter language as a decimal number.
1756 /// </summary>
1757 /// <param name="sourceLineNumbers">Source line information about the owner element.</param>
1758 /// <param name="attribute">The attribute containing the value to get.</param>
1759 /// <returns>Numeric representation of the language as per WinNT.h.</returns>
1760 private int GetPerformanceCounterLanguage(SourceLineNumber sourceLineNumbers, XAttribute attribute)
1761 {
1762 int language = 0;
1763 if (String.Empty == attribute.Value)
1764 {
1765 this.Messaging.Write(ErrorMessages.IllegalEmptyAttributeValue(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName));
1766 }
1767 else
1768 {
1769 switch (attribute.Value)
1770 {
1771 case "afrikaans":
1772 language = 0x36;
1773 break;
1774 case "albanian":
1775 language = 0x1c;
1776 break;
1777 case "arabic":
1778 language = 0x01;
1779 break;
1780 case "armenian":
1781 language = 0x2b;
1782 break;
1783 case "assamese":
1784 language = 0x4d;
1785 break;
1786 case "azeri":
1787 language = 0x2c;
1788 break;
1789 case "basque":
1790 language = 0x2d;
1791 break;
1792 case "belarusian":
1793 language = 0x23;
1794 break;
1795 case "bengali":
1796 language = 0x45;
1797 break;
1798 case "bulgarian":
1799 language = 0x02;
1800 break;
1801 case "catalan":
1802 language = 0x03;
1803 break;
1804 case "chinese":
1805 language = 0x04;
1806 break;
1807 case "croatian":
1808 language = 0x1a;
1809 break;
1810 case "czech":
1811 language = 0x05;
1812 break;
1813 case "danish":
1814 language = 0x06;
1815 break;
1816 case "divehi":
1817 language = 0x65;
1818 break;
1819 case "dutch":
1820 language = 0x13;
1821 break;
1822 case "piglatin":
1823 case "english":
1824 language = 0x09;
1825 break;
1826 case "estonian":
1827 language = 0x25;
1828 break;
1829 case "faeroese":
1830 language = 0x38;
1831 break;
1832 case "farsi":
1833 language = 0x29;
1834 break;
1835 case "finnish":
1836 language = 0x0b;
1837 break;
1838 case "french":
1839 language = 0x0c;
1840 break;
1841 case "galician":
1842 language = 0x56;
1843 break;
1844 case "georgian":
1845 language = 0x37;
1846 break;
1847 case "german":
1848 language = 0x07;
1849 break;
1850 case "greek":
1851 language = 0x08;
1852 break;
1853 case "gujarati":
1854 language = 0x47;
1855 break;
1856 case "hebrew":
1857 language = 0x0d;
1858 break;
1859 case "hindi":
1860 language = 0x39;
1861 break;
1862 case "hungarian":
1863 language = 0x0e;
1864 break;
1865 case "icelandic":
1866 language = 0x0f;
1867 break;
1868 case "indonesian":
1869 language = 0x21;
1870 break;
1871 case "italian":
1872 language = 0x10;
1873 break;
1874 case "japanese":
1875 language = 0x11;
1876 break;
1877 case "kannada":
1878 language = 0x4b;
1879 break;
1880 case "kashmiri":
1881 language = 0x60;
1882 break;
1883 case "kazak":
1884 language = 0x3f;
1885 break;
1886 case "konkani":
1887 language = 0x57;
1888 break;
1889 case "korean":
1890 language = 0x12;
1891 break;
1892 case "kyrgyz":
1893 language = 0x40;
1894 break;
1895 case "latvian":
1896 language = 0x26;
1897 break;
1898 case "lithuanian":
1899 language = 0x27;
1900 break;
1901 case "macedonian":
1902 language = 0x2f;
1903 break;
1904 case "malay":
1905 language = 0x3e;
1906 break;
1907 case "malayalam":
1908 language = 0x4c;
1909 break;
1910 case "manipuri":
1911 language = 0x58;
1912 break;
1913 case "marathi":
1914 language = 0x4e;
1915 break;
1916 case "mongolian":
1917 language = 0x50;
1918 break;
1919 case "nepali":
1920 language = 0x61;
1921 break;
1922 case "norwegian":
1923 language = 0x14;
1924 break;
1925 case "oriya":
1926 language = 0x48;
1927 break;
1928 case "polish":
1929 language = 0x15;
1930 break;
1931 case "portuguese":
1932 language = 0x16;
1933 break;
1934 case "punjabi":
1935 language = 0x46;
1936 break;
1937 case "romanian":
1938 language = 0x18;
1939 break;
1940 case "russian":
1941 language = 0x19;
1942 break;
1943 case "sanskrit":
1944 language = 0x4f;
1945 break;
1946 case "serbian":
1947 language = 0x1a;
1948 break;
1949 case "sindhi":
1950 language = 0x59;
1951 break;
1952 case "slovak":
1953 language = 0x1b;
1954 break;
1955 case "slovenian":
1956 language = 0x24;
1957 break;
1958 case "spanish":
1959 language = 0x0a;
1960 break;
1961 case "swahili":
1962 language = 0x41;
1963 break;
1964 case "swedish":
1965 language = 0x1d;
1966 break;
1967 case "syriac":
1968 language = 0x5a;
1969 break;
1970 case "tamil":
1971 language = 0x49;
1972 break;
1973 case "tatar":
1974 language = 0x44;
1975 break;
1976 case "telugu":
1977 language = 0x4a;
1978 break;
1979 case "thai":
1980 language = 0x1e;
1981 break;
1982 case "turkish":
1983 language = 0x1f;
1984 break;
1985 case "ukrainian":
1986 language = 0x22;
1987 break;
1988 case "urdu":
1989 language = 0x20;
1990 break;
1991 case "uzbek":
1992 language = 0x43;
1993 break;
1994 case "vietnamese":
1995 language = 0x2a;
1996 break;
1997 default:
1998 this.Messaging.Write(ErrorMessages.IllegalEmptyAttributeValue(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName));
1999 break;
2000 }
2001 }
2002
2003 return language;
2004 }
2005
2006 /// <summary>
2007 /// Parses a performance counter element.
2008 /// </summary>
2009 /// <param name="element">Element to parse.</param>
2010 /// <param name="defaultLanguage">Default language for the performance counter.</param>
2011 private ParsedPerformanceCounter ParsePerformanceCounterElement(Intermediate intermediate, IntermediateSection section, XElement element, int defaultLanguage)
2012 {
2013 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element);
2014 ParsedPerformanceCounter parsedPerformanceCounter = null;
2015 string name = null;
2016 string help = null;
2017 var type = System.Diagnostics.PerformanceCounterType.NumberOfItems32;
2018 int language = defaultLanguage;
2019
2020 foreach (var attrib in element.Attributes())
2021 {
2022 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
2023 {
2024 switch (attrib.Name.LocalName)
2025 {
2026 case "Help":
2027 help = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
2028 break;
2029 case "Name":
2030 name = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
2031 break;
2032 case "Type":
2033 type = this.GetPerformanceCounterType(sourceLineNumbers, attrib);
2034 break;
2035 case "Language":
2036 language = this.GetPerformanceCounterLanguage(sourceLineNumbers, attrib);
2037 break;
2038 default:
2039 this.ParseHelper.UnexpectedAttribute(element, attrib);
2040 break;
2041 }
2042 }
2043 else
2044 {
2045 this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib);
2046 }
2047 }
2048
2049 if (null == name)
2050 {
2051 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Name"));
2052 }
2053
2054 if (null == help)
2055 {
2056 this.Messaging.Write(UtilWarnings.RequiredAttributeForWindowsXP(sourceLineNumbers, element.Name.LocalName, "Help"));
2057 }
2058
2059 this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element);
2060
2061 if (!this.Messaging.EncounteredError)
2062 {
2063 parsedPerformanceCounter = new ParsedPerformanceCounter(name, help, type, language);
2064 }
2065
2066 return parsedPerformanceCounter;
2067 }
2068
2069 /// <summary>
2070 /// Gets the performance counter type.
2071 /// </summary>
2072 /// <param name="sourceLineNumbers">Source line information about the owner element.</param>
2073 /// <param name="attribute">The attribute containing the value to get.</param>
2074 /// <returns>Numeric representation of the language as per WinNT.h.</returns>
2075 private System.Diagnostics.PerformanceCounterType GetPerformanceCounterType(SourceLineNumber sourceLineNumbers, XAttribute attribute)
2076 {
2077 var type = System.Diagnostics.PerformanceCounterType.NumberOfItems32;
2078 if (String.Empty == attribute.Value)
2079 {
2080 this.Messaging.Write(ErrorMessages.IllegalEmptyAttributeValue(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName));
2081 }
2082 else
2083 {
2084 switch (attribute.Value)
2085 {
2086 case "averageBase":
2087 type = System.Diagnostics.PerformanceCounterType.AverageBase;
2088 break;
2089 case "averageCount64":
2090 type = System.Diagnostics.PerformanceCounterType.AverageCount64;
2091 break;
2092 case "averageTimer32":
2093 type = System.Diagnostics.PerformanceCounterType.AverageTimer32;
2094 break;
2095 case "counterDelta32":
2096 type = System.Diagnostics.PerformanceCounterType.CounterDelta32;
2097 break;
2098 case "counterTimerInverse":
2099 type = System.Diagnostics.PerformanceCounterType.CounterTimerInverse;
2100 break;
2101 case "sampleFraction":
2102 type = System.Diagnostics.PerformanceCounterType.SampleFraction;
2103 break;
2104 case "timer100Ns":
2105 type = System.Diagnostics.PerformanceCounterType.Timer100Ns;
2106 break;
2107 case "counterTimer":
2108 type = System.Diagnostics.PerformanceCounterType.CounterTimer;
2109 break;
2110 case "rawFraction":
2111 type = System.Diagnostics.PerformanceCounterType.RawFraction;
2112 break;
2113 case "timer100NsInverse":
2114 type = System.Diagnostics.PerformanceCounterType.Timer100NsInverse;
2115 break;
2116 case "counterMultiTimer":
2117 type = System.Diagnostics.PerformanceCounterType.CounterMultiTimer;
2118 break;
2119 case "counterMultiTimer100Ns":
2120 type = System.Diagnostics.PerformanceCounterType.CounterMultiTimer100Ns;
2121 break;
2122 case "counterMultiTimerInverse":
2123 type = System.Diagnostics.PerformanceCounterType.CounterMultiTimerInverse;
2124 break;
2125 case "counterMultiTimer100NsInverse":
2126 type = System.Diagnostics.PerformanceCounterType.CounterMultiTimer100NsInverse;
2127 break;
2128 case "elapsedTime":
2129 type = System.Diagnostics.PerformanceCounterType.ElapsedTime;
2130 break;
2131 case "sampleBase":
2132 type = System.Diagnostics.PerformanceCounterType.SampleBase;
2133 break;
2134 case "rawBase":
2135 type = System.Diagnostics.PerformanceCounterType.RawBase;
2136 break;
2137 case "counterMultiBase":
2138 type = System.Diagnostics.PerformanceCounterType.CounterMultiBase;
2139 break;
2140 case "rateOfCountsPerSecond64":
2141 type = System.Diagnostics.PerformanceCounterType.RateOfCountsPerSecond64;
2142 break;
2143 case "rateOfCountsPerSecond32":
2144 type = System.Diagnostics.PerformanceCounterType.RateOfCountsPerSecond32;
2145 break;
2146 case "countPerTimeInterval64":
2147 type = System.Diagnostics.PerformanceCounterType.CountPerTimeInterval64;
2148 break;
2149 case "countPerTimeInterval32":
2150 type = System.Diagnostics.PerformanceCounterType.CountPerTimeInterval32;
2151 break;
2152 case "sampleCounter":
2153 type = System.Diagnostics.PerformanceCounterType.SampleCounter;
2154 break;
2155 case "counterDelta64":
2156 type = System.Diagnostics.PerformanceCounterType.CounterDelta64;
2157 break;
2158 case "numberOfItems64":
2159 type = System.Diagnostics.PerformanceCounterType.NumberOfItems64;
2160 break;
2161 case "numberOfItems32":
2162 type = System.Diagnostics.PerformanceCounterType.NumberOfItems32;
2163 break;
2164 case "numberOfItemsHEX64":
2165 type = System.Diagnostics.PerformanceCounterType.NumberOfItemsHEX64;
2166 break;
2167 case "numberOfItemsHEX32":
2168 type = System.Diagnostics.PerformanceCounterType.NumberOfItemsHEX32;
2169 break;
2170 default:
2171 this.Messaging.Write(ErrorMessages.IllegalEmptyAttributeValue(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName));
2172 break;
2173 }
2174 }
2175
2176 return type;
2177 }
2178
2179 /// <summary>
2180 /// Parses a perf counter element.
2181 /// </summary>
2182 /// <param name="element">Element to parse.</param>
2183 /// <param name="componentId">Identifier of parent component.</param>
2184 /// <param name="fileId">Identifier of referenced file.</param>
2185 private void ParsePerfCounterElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId, string fileId)
2186 {
2187 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element);
2188 string name = null;
2189
2190 this.Messaging.Write(UtilWarnings.DeprecatedPerfCounterElement(sourceLineNumbers));
2191
2192 foreach (var attrib in element.Attributes())
2193 {
2194 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
2195 {
2196 switch (attrib.Name.LocalName)
2197 {
2198 case "Name":
2199 name = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
2200 break;
2201 default:
2202 this.ParseHelper.UnexpectedAttribute(element, attrib);
2203 break;
2204 }
2205 }
2206 else
2207 {
2208 this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib);
2209 }
2210 }
2211
2212 if (null == name)
2213 {
2214 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Name"));
2215 }
2216
2217 this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element);
2218
2219 if (!this.Messaging.EncounteredError)
2220 {
2221 section.AddSymbol(new PerfmonSymbol(sourceLineNumbers)
2222 {
2223 ComponentRef = componentId,
2224 File = $"[#{fileId}]",
2225 Name = name,
2226 });
2227 }
2228
2229 this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4ConfigurePerfmonInstall", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64);
2230 this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4ConfigurePerfmonUninstall", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64);
2231 }
2232
2233
2234 /// <summary>
2235 /// Parses a perf manifest element.
2236 /// </summary>
2237 /// <param name="element">Element to parse.</param>
2238 /// <param name="componentId">Identifier of parent component.</param>
2239 /// <param name="fileId">Identifier of referenced file.</param>
2240 private void ParsePerfCounterManifestElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId, string fileId)
2241 {
2242 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element);
2243 string resourceFileDirectory = null;
2244
2245 foreach (var attrib in element.Attributes())
2246 {
2247 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
2248 {
2249 switch (attrib.Name.LocalName)
2250 {
2251 case "ResourceFileDirectory":
2252 resourceFileDirectory = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
2253 break;
2254 default:
2255 this.ParseHelper.UnexpectedAttribute(element, attrib);
2256 break;
2257 }
2258 }
2259 else
2260 {
2261 this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib);
2262 }
2263 }
2264
2265 this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element);
2266
2267 if (!this.Messaging.EncounteredError)
2268 {
2269 section.AddSymbol(new PerfmonManifestSymbol(sourceLineNumbers)
2270 {
2271 ComponentRef = componentId,
2272 File = $"[#{fileId}]",
2273 ResourceFileDirectory = resourceFileDirectory,
2274 });
2275 }
2276
2277 this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4ConfigurePerfmonManifestRegister", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64);
2278 this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4ConfigurePerfmonManifestUnregister", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64);
2279 }
2280
2281 /// <summary>
2282 /// Parses a format files element.
2283 /// </summary>
2284 /// <param name="element">Element to parse.</param>
2285 /// <param name="fileId">Identifier of referenced file.</param>
2286 /// <param name="win64">Flag to determine whether the component is 64-bit.</param>
2287 private void ParseFormatFileElement(Intermediate intermediate, IntermediateSection section, XElement element, string fileId, bool win64)
2288 {
2289 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element);
2290 string binaryId = null;
2291
2292 foreach (var attrib in element.Attributes())
2293 {
2294 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
2295 {
2296 switch (attrib.Name.LocalName)
2297 {
2298 case "BinaryRef":
2299 binaryId = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
2300 break;
2301 default:
2302 this.ParseHelper.UnexpectedAttribute(element, attrib);
2303 break;
2304 }
2305 }
2306 else
2307 {
2308 this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib);
2309 }
2310 }
2311
2312 this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element);
2313
2314 if (null == binaryId)
2315 {
2316 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "BinaryRef"));
2317 }
2318
2319 if (!this.Messaging.EncounteredError)
2320 {
2321 this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4SchedFormatFiles", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64);
2322
2323 section.AddSymbol(new WixFormatFilesSymbol(sourceLineNumbers)
2324 {
2325 BinaryRef = binaryId,
2326 FileRef = fileId,
2327 });
2328
2329 this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, SymbolDefinitions.Binary, binaryId);
2330 }
2331 }
2332
2333 /// <summary>
2334 /// Parses a event manifest element.
2335 /// </summary>
2336 /// <param name="element">Element to parse.</param>
2337 /// <param name="componentId">Identifier of parent component.</param>
2338 /// <param name="fileId">Identifier of referenced file.</param>
2339 private void ParseEventManifestElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId, string fileId)
2340 {
2341 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element);
2342 string messageFile = null;
2343 string resourceFile = null;
2344 string parameterFile = null;
2345
2346 foreach (var attrib in element.Attributes())
2347 {
2348 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
2349 {
2350 switch (attrib.Name.LocalName)
2351 {
2352 case "MessageFile":
2353 messageFile = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
2354 break;
2355 case "ResourceFile":
2356 resourceFile = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
2357 break;
2358 case "ParameterFile":
2359 parameterFile = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
2360 break;
2361 default:
2362 this.ParseHelper.UnexpectedAttribute(element, attrib);
2363 break;
2364 }
2365 }
2366 else
2367 {
2368 this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib);
2369 }
2370 }
2371
2372 this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element);
2373
2374 if (!this.Messaging.EncounteredError)
2375 {
2376 section.AddSymbol(new EventManifestSymbol(sourceLineNumbers)
2377 {
2378 ComponentRef = componentId,
2379 File = $"[#{fileId}]",
2380 });
2381
2382 if (null != messageFile)
2383 {
2384 section.AddSymbol(new XmlFileSymbol(sourceLineNumbers, new Identifier(AccessModifier.Section, $"Config_{fileId}MessageFile"))
2385 {
2386 File = $"[#{fileId}]",
2387 ElementPath = "/*/*/*/*[\\[]@messageFileName[\\]]",
2388 Name = "messageFileName",
2389 Value = messageFile,
2390 Flags = 4 | 0x00001000, //bulk write | preserve modified date
2391 ComponentRef = componentId,
2392 });
2393 }
2394 if (null != parameterFile)
2395 {
2396 section.AddSymbol(new XmlFileSymbol(sourceLineNumbers, new Identifier(AccessModifier.Section, $"Config_{fileId}ParameterFile"))
2397 {
2398 File = $"[#{fileId}]",
2399 ElementPath = "/*/*/*/*[\\[]@parameterFileName[\\]]",
2400 Name = "parameterFileName",
2401 Value = parameterFile,
2402 Flags = 4 | 0x00001000, //bulk write | preserve modified date
2403 ComponentRef = componentId,
2404 });
2405 }
2406 if (null != resourceFile)
2407 {
2408 section.AddSymbol(new XmlFileSymbol(sourceLineNumbers, new Identifier(AccessModifier.Section, $"Config_{fileId}ResourceFile"))
2409 {
2410 File = $"[#{fileId}]",
2411 ElementPath = "/*/*/*/*[\\[]@resourceFileName[\\]]",
2412 Name = "resourceFileName",
2413 Value = resourceFile,
2414 Flags = 4 | 0x00001000, //bulk write | preserve modified date
2415 ComponentRef = componentId,
2416 });
2417 }
2418
2419 }
2420
2421 this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4ConfigureEventManifestRegister", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64);
2422 this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4ConfigureEventManifestUnregister", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64);
2423
2424 if (null != messageFile || null != parameterFile || null != resourceFile)
2425 {
2426 this.AddReferenceToSchedXmlFile(sourceLineNumbers, section);
2427 }
2428 }
2429
2430 /// <summary>
2431 /// Parses a PermissionEx element.
2432 /// </summary>
2433 /// <param name="element">Element to parse.</param>
2434 /// <param name="objectId">Identifier of object to be secured.</param>
2435 /// <param name="componentId">Identifier of component, used to determine install state.</param>
2436 /// <param name="win64">Flag to determine whether the component is 64-bit.</param>
2437 /// <param name="tableName">Name of table that contains objectId.</param>
2438 private void ParsePermissionExElement(Intermediate intermediate, IntermediateSection section, XElement element, string objectId, string componentId, bool win64, string tableName)
2439 {
2440 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element);
2441 var bits = new BitArray(32);
2442 string domain = null;
2443 string[] specialPermissions = null;
2444 string user = null;
2445 var attributes = WixPermissionExAttributes.Inheritable; // default to inheritable.
2446
2447 var permissionType = PermissionType.SecureObjects;
2448
2449 switch (tableName)
2450 {
2451 case "CreateFolder":
2452 specialPermissions = UtilConstants.FolderPermissions;
2453 break;
2454 case "File":
2455 specialPermissions = UtilConstants.FilePermissions;
2456 break;
2457 case "Registry":
2458 specialPermissions = UtilConstants.RegistryPermissions;
2459 if (String.IsNullOrEmpty(objectId))
2460 {
2461 this.Messaging.Write(UtilErrors.InvalidRegistryObject(sourceLineNumbers, element.Parent.Name.LocalName));
2462 }
2463 break;
2464 case "ServiceInstall":
2465 specialPermissions = UtilConstants.ServicePermissions;
2466 permissionType = PermissionType.SecureObjects;
2467 break;
2468 default:
2469 this.ParseHelper.UnexpectedElement(element.Parent, element);
2470 break;
2471 }
2472
2473 foreach (var attrib in element.Attributes())
2474 {
2475 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
2476 {
2477 switch (attrib.Name.LocalName)
2478 {
2479 case "Domain":
2480 if (PermissionType.FileSharePermissions == permissionType)
2481 {
2482 this.Messaging.Write(ErrorMessages.IllegalAttributeWhenNested(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName, element.Parent.Name.LocalName));
2483 }
2484 domain = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
2485 break;
2486 case "Inheritable":
2487 if (this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib) == YesNoType.No)
2488 {
2489 attributes &= ~WixPermissionExAttributes.Inheritable;
2490 }
2491 break;
2492 case "User":
2493 user = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
2494 break;
2495 default:
2496 var attribValue = this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib);
2497 if (!this.TrySetBitFromName(UtilConstants.StandardPermissions, attrib.Name.LocalName, attribValue, bits, 16))
2498 {
2499 if (!this.TrySetBitFromName(UtilConstants.GenericPermissions, attrib.Name.LocalName, attribValue, bits, 28))
2500 {
2501 if (!this.TrySetBitFromName(specialPermissions, attrib.Name.LocalName, attribValue, bits, 0))
2502 {
2503 this.ParseHelper.UnexpectedAttribute(element, attrib);
2504 break;
2505 }
2506 }
2507 }
2508 break;
2509 }
2510 }
2511 else
2512 {
2513 this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib);
2514 }
2515 }
2516
2517 var permission = this.CreateIntegerFromBitArray(bits);
2518
2519 if (null == user)
2520 {
2521 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "User"));
2522 }
2523
2524 if (Int32.MinValue == permission) // just GENERIC_READ, which is MSI_NULL
2525 {
2526 this.Messaging.Write(ErrorMessages.GenericReadNotAllowed(sourceLineNumbers));
2527 }
2528
2529 this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element);
2530
2531 if (!this.Messaging.EncounteredError)
2532 {
2533 this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4SchedSecureObjects", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64);
2534
2535 var id = this.ParseHelper.CreateIdentifier("sec", objectId, tableName, domain, user);
2536 section.AddSymbol(new SecureObjectsSymbol(sourceLineNumbers, id)
2537 {
2538 SecureObject = objectId,
2539 Table = tableName,
2540 Domain = domain,
2541 User = user,
2542 Attributes = attributes,
2543 Permission = permission,
2544 ComponentRef = componentId,
2545 });
2546 }
2547 }
2548
2549 /// <summary>
2550 /// Parses a ProductSearch element.
2551 /// </summary>
2552 /// <param name="element">Element to parse.</param>
2553 private void ParseProductSearchElement(Intermediate intermediate, IntermediateSection section, XElement element)
2554 {
2555 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element);
2556 Identifier id = null;
2557 string variable = null;
2558 string condition = null;
2559 string after = null;
2560 string productCode = null;
2561 string upgradeCode = null;
2562 var attributes = WixProductSearchAttributes.Version;
2563
2564 foreach (var attrib in element.Attributes())
2565 {
2566 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
2567 {
2568 switch (attrib.Name.LocalName)
2569 {
2570 case "Id":
2571 case "Variable":
2572 case "Condition":
2573 case "After":
2574 this.ParseCommonSearchAttributes(sourceLineNumbers, attrib, ref id, ref variable, ref condition, ref after);
2575 break;
2576 case "ProductCode":
2577 productCode = this.ParseHelper.GetAttributeGuidValue(sourceLineNumbers, attrib, false);
2578 break;
2579 case "UpgradeCode":
2580 upgradeCode = this.ParseHelper.GetAttributeGuidValue(sourceLineNumbers, attrib, false);
2581 break;
2582 case "Result":
2583 var result = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
2584 switch (result)
2585 {
2586 case "version":
2587 attributes = WixProductSearchAttributes.Version;
2588 break;
2589 case "language":
2590 attributes = WixProductSearchAttributes.Language;
2591 break;
2592 case "state":
2593 attributes = WixProductSearchAttributes.State;
2594 break;
2595 case "assignment":
2596 attributes = WixProductSearchAttributes.Assignment;
2597 break;
2598 default:
2599 this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, attrib.Parent.Name.LocalName, attrib.Name.LocalName, result, "version", "language", "state", "assignment"));
2600 break;
2601 }
2602 break;
2603 default:
2604 this.ParseHelper.UnexpectedAttribute(element, attrib);
2605 break;
2606 }
2607 }
2608 else
2609 {
2610 this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib);
2611 }
2612 }
2613
2614 if (null == upgradeCode && null == productCode)
2615 {
2616 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "ProductCode", "UpgradeCode", true));
2617 }
2618
2619 if (null != upgradeCode && null != productCode)
2620 {
2621 this.Messaging.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, element.Name.LocalName, "UpgradeCode", "ProductCode"));
2622 }
2623
2624 if (null == id)
2625 {
2626 id = this.ParseHelper.CreateIdentifier("wps", variable, condition, after, (productCode == null ? upgradeCode : productCode), attributes.ToString());
2627 }
2628
2629 this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element);
2630
2631 this.ParseHelper.CreateWixSearchSymbol(section, sourceLineNumbers, element.Name.LocalName, id, variable, condition, after, null);
2632
2633 if (!this.Messaging.EncounteredError)
2634 {
2635 // set an additional flag if this is an upgrade code
2636 if (null != upgradeCode)
2637 {
2638 attributes |= WixProductSearchAttributes.UpgradeCode;
2639 }
2640
2641 section.AddSymbol(new WixProductSearchSymbol(sourceLineNumbers, id)
2642 {
2643 Guid = productCode ?? upgradeCode,
2644 Attributes = attributes,
2645 });
2646 }
2647 }
2648
2649 /// <summary>
2650 /// Parses a RegistrySearch element.
2651 /// </summary>
2652 /// <param name="element">Element to parse.</param>
2653 private void ParseRegistrySearchElement(Intermediate intermediate, IntermediateSection section, XElement element)
2654 {
2655 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element);
2656 Identifier id = null;
2657 string variable = null;
2658 string condition = null;
2659 string after = null;
2660 RegistryRootType? root = null;
2661 string key = null;
2662 string value = null;
2663 var expand = YesNoType.NotSet;
2664 var win64 = this.Context.IsCurrentPlatform64Bit;
2665 var attributes = WixRegistrySearchAttributes.Raw | WixRegistrySearchAttributes.WantValue;
2666
2667 foreach (var attrib in element.Attributes())
2668 {
2669 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
2670 {
2671 switch (attrib.Name.LocalName)
2672 {
2673 case "Id":
2674 case "Variable":
2675 case "Condition":
2676 case "After":
2677 this.ParseCommonSearchAttributes(sourceLineNumbers, attrib, ref id, ref variable, ref condition, ref after);
2678 break;
2679 case "Bitness":
2680 var bitnessValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
2681 switch (bitnessValue)
2682 {
2683 case "always32":
2684 win64 = false;
2685 break;
2686 case "always64":
2687 win64 = true;
2688 break;
2689 case "default":
2690 case "":
2691 break;
2692 default:
2693 this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, attrib.Name.LocalName, attrib.Name.LocalName, bitnessValue, "default", "always32", "always64"));
2694 break;
2695 }
2696 break;
2697 case "Root":
2698 root = this.ParseHelper.GetAttributeRegistryRootValue(sourceLineNumbers, attrib, false);
2699 break;
2700 case "Key":
2701 key = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
2702 break;
2703 case "Value":
2704 value = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
2705 break;
2706 case "ExpandEnvironmentVariables":
2707 expand = this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib);
2708 break;
2709 case "Format":
2710 string format = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
2711 switch (format)
2712 {
2713 case "raw":
2714 attributes |= WixRegistrySearchAttributes.Raw;
2715 break;
2716 case "compatible":
2717 attributes |= WixRegistrySearchAttributes.Compatible;
2718 break;
2719 default:
2720 this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, attrib.Parent.Name.LocalName, attrib.Name.LocalName, format, "raw", "compatible"));
2721 break;
2722 }
2723 break;
2724 case "Result":
2725 var result = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
2726 switch (result)
2727 {
2728 case "exists":
2729 attributes |= WixRegistrySearchAttributes.WantExists;
2730 break;
2731 case "value":
2732 attributes |= WixRegistrySearchAttributes.WantValue;
2733 break;
2734 default:
2735 this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, attrib.Parent.Name.LocalName, attrib.Name.LocalName, result, "exists", "value"));
2736 break;
2737 }
2738 break;
2739 default:
2740 this.ParseHelper.UnexpectedAttribute(element, attrib);
2741 break;
2742 }
2743 }
2744 else
2745 {
2746 this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib);
2747 }
2748 }
2749
2750 if (!root.HasValue)
2751 {
2752 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Root"));
2753 }
2754
2755 if (null == key)
2756 {
2757 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Key"));
2758 }
2759
2760 if (null == id)
2761 {
2762 id = this.ParseHelper.CreateIdentifier("wrs", variable, condition, after, root.ToString(), key, value, attributes.ToString());
2763 }
2764
2765 if (expand == YesNoType.Yes)
2766 {
2767 if (0 != (attributes & WixRegistrySearchAttributes.WantExists))
2768 {
2769 this.Messaging.Write(ErrorMessages.IllegalAttributeValueWithOtherAttribute(sourceLineNumbers, element.Name.LocalName, "ExpandEnvironmentVariables", expand.ToString(), "Result", "exists"));
2770 }
2771
2772 attributes |= WixRegistrySearchAttributes.ExpandEnvironmentVariables;
2773 }
2774
2775 if (win64)
2776 {
2777 attributes |= WixRegistrySearchAttributes.Win64;
2778 }
2779
2780 this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element);
2781
2782 this.ParseHelper.CreateWixSearchSymbol(section, sourceLineNumbers, element.Name.LocalName, id, variable, condition, after, null);
2783
2784 if (!this.Messaging.EncounteredError)
2785 {
2786 section.AddSymbol(new WixRegistrySearchSymbol(sourceLineNumbers, id)
2787 {
2788 Root = root.Value,
2789 Key = key,
2790 Value = value,
2791 Attributes = attributes,
2792 });
2793 }
2794 }
2795
2796 /// <summary>
2797 /// Parses a RemoveFolderEx element.
2798 /// </summary>
2799 /// <param name="element">Element to parse.</param>
2800 /// <param name="componentId">Identifier of parent component.</param>
2801 private void ParseRemoveFolderExElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId)
2802 {
2803 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element);
2804 Identifier id = null;
2805 var mode = WixRemoveFolderExInstallMode.Uninstall;
2806 string property = null;
2807 string condition = null;
2808
2809 foreach (var attrib in element.Attributes())
2810 {
2811 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
2812 {
2813 switch (attrib.Name.LocalName)
2814 {
2815 case "Condition":
2816 condition = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
2817 break;
2818 case "Id":
2819 id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib);
2820 break;
2821 case "On":
2822 var onValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
2823 if (onValue.Length == 0)
2824 {
2825 }
2826 else
2827 {
2828 switch (onValue)
2829 {
2830 case "install":
2831 mode = WixRemoveFolderExInstallMode.Install;
2832 break;
2833 case "uninstall":
2834 mode = WixRemoveFolderExInstallMode.Uninstall;
2835 break;
2836 case "both":
2837 mode = WixRemoveFolderExInstallMode.Both;
2838 break;
2839 default:
2840 this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, "On", onValue, "install", "uninstall", "both"));
2841 break;
2842 }
2843 }
2844 break;
2845 case "Property":
2846 property = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
2847 break;
2848 default:
2849 this.ParseHelper.UnexpectedAttribute(element, attrib);
2850 break;
2851 }
2852 }
2853 else
2854 {
2855 this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib);
2856 }
2857 }
2858
2859 if (String.IsNullOrEmpty(property))
2860 {
2861 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Property"));
2862 }
2863
2864 if (id == null)
2865 {
2866 id = this.ParseHelper.CreateIdentifier("wrf", componentId, property, mode.ToString());
2867 }
2868
2869 this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element);
2870
2871 if (!this.Messaging.EncounteredError)
2872 {
2873 this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4RemoveFoldersEx", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64);
2874
2875 section.AddSymbol(new WixRemoveFolderExSymbol(sourceLineNumbers, id)
2876 {
2877 ComponentRef = componentId,
2878 Property = property,
2879 InstallMode = mode,
2880 Condition = condition
2881 });
2882
2883 this.ParseHelper.EnsureTable(section, sourceLineNumbers, "RemoveFile");
2884 }
2885 }
2886
2887 /// <summary>
2888 /// Parses a RemoveRegistryKeyEx element.
2889 /// </summary>
2890 /// <param name="node">Element to parse.</param>
2891 /// <param name="componentId">Identifier of parent component.</param>
2892 private void ParseRemoveRegistryKeyExElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId)
2893 {
2894 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element);
2895 Identifier id = null;
2896 var mode = WixRemoveRegistryKeyExInstallMode.Uninstall;
2897 string condition = null;
2898 RegistryRootType? root = null;
2899 string key = null;
2900
2901 foreach (var attrib in element.Attributes())
2902 {
2903 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
2904 {
2905 switch (attrib.Name.LocalName)
2906 {
2907 case "Condition":
2908 condition = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
2909 break;
2910 case "Id":
2911 id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib);
2912 break;
2913 case "On":
2914 var actionValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
2915 switch (actionValue)
2916 {
2917 case "":
2918 break;
2919 case "install":
2920 mode = WixRemoveRegistryKeyExInstallMode.Install;
2921 break;
2922 case "uninstall":
2923 mode = WixRemoveRegistryKeyExInstallMode.Uninstall;
2924 break;
2925 default:
2926 this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, "On", actionValue, "install", "uninstall"));
2927 break;
2928 }
2929 break;
2930 case "Root":
2931 root = this.ParseHelper.GetAttributeRegistryRootValue(sourceLineNumbers, attrib, false);
2932 break;
2933 case "Key":
2934 key = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
2935 break;
2936 default:
2937 this.ParseHelper.UnexpectedAttribute(element, attrib);
2938 break;
2939 }
2940 }
2941 else
2942 {
2943 this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib);
2944 }
2945 }
2946
2947 if (!root.HasValue)
2948 {
2949 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Root"));
2950 }
2951
2952 if (key == null)
2953 {
2954 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Key"));
2955 }
2956
2957 if (id == null)
2958 {
2959 id = this.ParseHelper.CreateIdentifier("rrx", componentId, condition, root.ToString(), key, mode.ToString());
2960 }
2961
2962 this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element);
2963
2964 if (!this.Messaging.EncounteredError)
2965 {
2966 this.ParseHelper.EnsureTable(section, sourceLineNumbers, "Registry");
2967 this.ParseHelper.EnsureTable(section, sourceLineNumbers, "RemoveRegistry");
2968 this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4RemoveRegistryKeysEx", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64);
2969
2970 section.AddSymbol(new WixRemoveRegistryKeyExSymbol(sourceLineNumbers, id)
2971 {
2972 ComponentRef = componentId,
2973 Root = root.Value,
2974 Key = key,
2975 InstallMode = mode,
2976 Condition = condition
2977 });
2978 }
2979 }
2980
2981 /// <summary>
2982 /// Parses a RestartResource element.
2983 /// </summary>
2984 /// <param name="element">The element to parse.</param>
2985 /// <param name="componentId">The identity of the parent component.</param>
2986 private void ParseRestartResourceElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId)
2987 {
2988 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element);
2989 Identifier id = null;
2990 string resource = null;
2991 WixRestartResourceAttributes? attributes = null;
2992
2993 foreach (var attrib in element.Attributes())
2994 {
2995 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
2996 {
2997 switch (attrib.Name.LocalName)
2998 {
2999 case "Id":
3000 id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib);
3001 break;
3002
3003 case "Path":
3004 resource = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
3005 attributes = WixRestartResourceAttributes.Filename;
3006 break;
3007
3008 case "ProcessName":
3009 resource = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
3010 attributes = WixRestartResourceAttributes.ProcessName;
3011 break;
3012
3013 case "ServiceName":
3014 resource = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
3015 attributes = WixRestartResourceAttributes.ServiceName;
3016 break;
3017
3018 default:
3019 this.ParseHelper.UnexpectedAttribute(element, attrib);
3020 break;
3021 }
3022 }
3023 else
3024 {
3025 this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib);
3026 }
3027 }
3028
3029 // Validate the attribute.
3030 if (id == null)
3031 {
3032 id = this.ParseHelper.CreateIdentifier("wrr", componentId, resource, attributes.ToString());
3033 }
3034
3035 if (!attributes.HasValue)
3036 {
3037 this.Messaging.Write(ErrorMessages.ExpectedAttributes(sourceLineNumbers, element.Name.LocalName, "Path", "ProcessName", "ServiceName"));
3038 }
3039
3040 this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element);
3041
3042 if (!this.Messaging.EncounteredError)
3043 {
3044 this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4RegisterRestartResources", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64);
3045
3046 section.AddSymbol(new WixRestartResourceSymbol(sourceLineNumbers, id)
3047 {
3048 ComponentRef = componentId,
3049 Resource = resource,
3050 Attributes = attributes,
3051 });
3052 }
3053 }
3054
3055 /// <summary>
3056 /// Parses a service configuration element.
3057 /// </summary>
3058 /// <param name="element">Element to parse.</param>
3059 /// <param name="componentId">Identifier of parent component.</param>
3060 /// <param name="parentTableName">Name of parent element.</param>
3061 /// <param name="parentTableServiceName">Optional name of service </param>
3062 private void ParseServiceConfigElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId, string parentTableName, string parentTableServiceName)
3063 {
3064 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element);
3065 string firstFailureActionType = null;
3066 var newService = false;
3067 string programCommandLine = null;
3068 string rebootMessage = null;
3069 var resetPeriod = CompilerConstants.IntegerNotSet;
3070 var restartServiceDelay = CompilerConstants.IntegerNotSet;
3071 string secondFailureActionType = null;
3072 string serviceName = null;
3073 string thirdFailureActionType = null;
3074
3075 foreach (var attrib in element.Attributes())
3076 {
3077 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
3078 {
3079 switch (attrib.Name.LocalName)
3080 {
3081 case "FirstFailureActionType":
3082 firstFailureActionType = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
3083 break;
3084 case "ProgramCommandLine":
3085 programCommandLine = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
3086 break;
3087 case "RebootMessage":
3088 rebootMessage = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
3089 break;
3090 case "ResetPeriodInDays":
3091 resetPeriod = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int32.MaxValue);
3092 break;
3093 case "RestartServiceDelayInSeconds":
3094 restartServiceDelay = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int32.MaxValue);
3095 break;
3096 case "SecondFailureActionType":
3097 secondFailureActionType = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
3098 break;
3099 case "ServiceName":
3100 serviceName = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
3101 break;
3102 case "ThirdFailureActionType":
3103 thirdFailureActionType = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
3104 break;
3105 default:
3106 this.ParseHelper.UnexpectedAttribute(element, attrib);
3107 break;
3108 }
3109 }
3110 else
3111 {
3112 this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib);
3113 }
3114 }
3115
3116 // if this element is a child of ServiceInstall then ignore the service name provided.
3117 if ("ServiceInstall" == parentTableName)
3118 {
3119 // TODO: the ServiceName attribute should not be allowed in this case (the overwriting behavior may confuse users)
3120 serviceName = parentTableServiceName;
3121 newService = true;
3122 }
3123 else
3124 {
3125 // not a child of ServiceInstall, so ServiceName must have been provided
3126 if (null == serviceName)
3127 {
3128 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "ServiceName"));
3129 }
3130 }
3131
3132 this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element);
3133
3134 if (!this.Messaging.EncounteredError)
3135 {
3136 this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4SchedServiceConfig", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64);
3137
3138 section.AddSymbol(new ServiceConfigSymbol(sourceLineNumbers)
3139 {
3140 ServiceName = serviceName,
3141 ComponentRef = componentId,
3142 NewService = newService ? 1 : 0,
3143 FirstFailureActionType = firstFailureActionType,
3144 SecondFailureActionType = secondFailureActionType,
3145 ThirdFailureActionType = thirdFailureActionType,
3146 ResetPeriodInDays = resetPeriod,
3147 RestartServiceDelayInSeconds = restartServiceDelay,
3148 ProgramCommandLine = programCommandLine,
3149 RebootMessage = rebootMessage,
3150 });
3151 }
3152 }
3153
3154 /// <summary>
3155 /// Parses a touch file element.
3156 /// </summary>
3157 /// <param name="element">Element to parse.</param>
3158 /// <param name="componentId">Identifier of parent component.</param>
3159 /// <param name="win64">Indicates whether the path is a 64-bit path.</param>
3160 private void ParseTouchFileElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId, bool win64)
3161 {
3162 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element);
3163 Identifier id = null;
3164 string path = null;
3165 var onInstall = YesNoType.NotSet;
3166 var onReinstall = YesNoType.NotSet;
3167 var onUninstall = YesNoType.NotSet;
3168 var nonvital = YesNoType.NotSet;
3169 int attributes = 0;
3170
3171 foreach (var attrib in element.Attributes())
3172 {
3173 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
3174 {
3175 switch (attrib.Name.LocalName)
3176 {
3177 case "Id":
3178 id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib);
3179 break;
3180 case "Path":
3181 path = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
3182 break;
3183 case "OnInstall":
3184 onInstall = this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib);
3185 break;
3186 case "OnReinstall":
3187 onReinstall = this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib);
3188 break;
3189 case "OnUninstall":
3190 onUninstall = this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib);
3191 break;
3192 case "Nonvital":
3193 nonvital = this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib);
3194 break;
3195 default:
3196 this.ParseHelper.UnexpectedAttribute(element, attrib);
3197 break;
3198 }
3199 }
3200 else
3201 {
3202 this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib);
3203 }
3204 }
3205
3206 if (null == path)
3207 {
3208 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Path"));
3209 }
3210
3211 // If none of the scheduling actions are set, default to touching on install and reinstall.
3212 if (YesNoType.NotSet == onInstall && YesNoType.NotSet == onReinstall && YesNoType.NotSet == onUninstall)
3213 {
3214 onInstall = YesNoType.Yes;
3215 onReinstall = YesNoType.Yes;
3216 }
3217
3218 attributes |= YesNoType.Yes == onInstall ? 0x1 : 0;
3219 attributes |= YesNoType.Yes == onReinstall ? 0x2 : 0;
3220 attributes |= YesNoType.Yes == onUninstall ? 0x4 : 0;
3221 attributes |= win64 ? 0x10 : 0;
3222 attributes |= YesNoType.Yes == nonvital ? 0 : 0x20;
3223
3224 if (null == id)
3225 {
3226 id = this.ParseHelper.CreateIdentifier("tf", path, attributes.ToString());
3227 }
3228
3229 this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element);
3230
3231 if (!this.Messaging.EncounteredError)
3232 {
3233 section.AddSymbol(new WixTouchFileSymbol(sourceLineNumbers, id)
3234 {
3235 ComponentRef = componentId,
3236 Path = path,
3237 Attributes = attributes,
3238 });
3239
3240 this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4TouchFileDuringInstall", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64);
3241 }
3242 }
3243
3244 /// <summary>
3245 /// Parses an user element.
3246 /// </summary>
3247 /// <param name="element">Element to parse.</param>
3248 /// <param name="componentId">Optional identifier of parent component.</param>
3249 private void ParseUserElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId)
3250 {
3251 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element);
3252 Identifier id = null;
3253 int attributes = 0;
3254 string domain = null;
3255 string name = null;
3256 string password = null;
3257
3258 foreach (var attrib in element.Attributes())
3259 {
3260 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
3261 {
3262 switch (attrib.Name.LocalName)
3263 {
3264 case "Id":
3265 id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib);
3266 break;
3267 case "CanNotChangePassword":
3268 if (null == componentId)
3269 {
3270 this.Messaging.Write(UtilErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName));
3271 }
3272
3273 if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib))
3274 {
3275 attributes |= UserPasswdCantChange;
3276 }
3277 break;
3278 case "CreateUser":
3279 if (null == componentId)
3280 {
3281 this.Messaging.Write(UtilErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName));
3282 }
3283
3284 if (YesNoType.No == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib))
3285 {
3286 attributes |= UserDontCreateUser;
3287 }
3288 break;
3289 case "Disabled":
3290 if (null == componentId)
3291 {
3292 this.Messaging.Write(UtilErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName));
3293 }
3294
3295 if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib))
3296 {
3297 attributes |= UserDisableAccount;
3298 }
3299 break;
3300 case "Domain":
3301 domain = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
3302 break;
3303 case "FailIfExists":
3304 if (null == componentId)
3305 {
3306 this.Messaging.Write(UtilErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName));
3307 }
3308
3309 if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib))
3310 {
3311 attributes |= UserFailIfExists;
3312 }
3313 break;
3314 case "LogonAsService":
3315 if (null == componentId)
3316 {
3317 this.Messaging.Write(UtilErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName));
3318 }
3319 if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib))
3320 {
3321 attributes |= UserLogonAsService;
3322 }
3323 break;
3324 case "LogonAsBatchJob":
3325 if (null == componentId)
3326 {
3327 this.Messaging.Write(UtilErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName));
3328 }
3329 if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib))
3330 {
3331 attributes |= UserLogonAsBatchJob;
3332 }
3333 break;
3334 case "Name":
3335 name = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
3336 break;
3337 case "Password":
3338 password = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
3339 break;
3340 case "PasswordExpired":
3341 if (null == componentId)
3342 {
3343 this.Messaging.Write(UtilErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName));
3344 }
3345
3346 if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib))
3347 {
3348 attributes |= UserPasswdChangeReqdOnLogin;
3349 }
3350 break;
3351 case "PasswordNeverExpires":
3352 if (null == componentId)
3353 {
3354 this.Messaging.Write(UtilErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName));
3355 }
3356
3357 if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib))
3358 {
3359 attributes |= UserDontExpirePasswrd;
3360 }
3361 break;
3362 case "RemoveOnUninstall":
3363 if (null == componentId)
3364 {
3365 this.Messaging.Write(UtilErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName));
3366 }
3367
3368 if (YesNoType.No == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib))
3369 {
3370 attributes |= UserDontRemoveOnUninstall;
3371 }
3372 break;
3373 case "UpdateIfExists":
3374 if (null == componentId)
3375 {
3376 this.Messaging.Write(UtilErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName));
3377 }
3378
3379 if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib))
3380 {
3381 attributes |= UserUpdateIfExists;
3382 }
3383 break;
3384 case "Vital":
3385 if (null == componentId)
3386 {
3387 this.Messaging.Write(UtilErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName));
3388 }
3389
3390 if (YesNoType.No == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib))
3391 {
3392 attributes |= UserNonVital;
3393 }
3394 break;
3395 default:
3396 this.ParseHelper.UnexpectedAttribute(element, attrib);
3397 break;
3398 }
3399 }
3400 else
3401 {
3402 this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib);
3403 }
3404 }
3405
3406 if (null == id)
3407 {
3408 id = this.ParseHelper.CreateIdentifier("usr", componentId, name);
3409 }
3410
3411 if (null == name)
3412 {
3413 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Name"));
3414 }
3415
3416 foreach (var child in element.Elements())
3417 {
3418 if (this.Namespace == child.Name.Namespace)
3419 {
3420 switch (child.Name.LocalName)
3421 {
3422 case "GroupRef":
3423 if (null == componentId)
3424 {
3425 var childSourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(child);
3426 this.Messaging.Write(UtilErrors.IllegalElementWithoutComponent(childSourceLineNumbers, child.Name.LocalName));
3427 }
3428
3429 this.ParseGroupRefElement(intermediate, section, child, id.Id);
3430 break;
3431 default:
3432 this.ParseHelper.UnexpectedElement(element, child);
3433 break;
3434 }
3435 }
3436 else
3437 {
3438 this.ParseHelper.ParseExtensionElement(this.Context.Extensions, intermediate, section, element, child);
3439 }
3440 }
3441
3442 if (null != componentId)
3443 {
3444 this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4ConfigureUsers", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64);
3445 }
3446
3447 if (!this.Messaging.EncounteredError)
3448 {
3449 section.AddSymbol(new UserSymbol(sourceLineNumbers, id)
3450 {
3451 ComponentRef = componentId,
3452 Name = name,
3453 Domain = domain,
3454 Password = password,
3455 Attributes = attributes,
3456 });
3457 }
3458 }
3459
3460 /// <summary>
3461 /// Parses a XmlFile element.
3462 /// </summary>
3463 /// <param name="element">Element to parse.</param>
3464 /// <param name="componentId">Identifier of parent component.</param>
3465 private void ParseXmlFileElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId)
3466 {
3467 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element);
3468 Identifier id = null;
3469 string file = null;
3470 string elementPath = null;
3471 string name = null;
3472 string value = null;
3473 int sequence = -1;
3474 int flags = 0;
3475
3476 foreach (var attrib in element.Attributes())
3477 {
3478 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
3479 {
3480 switch (attrib.Name.LocalName)
3481 {
3482 case "Action":
3483 var actionValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
3484 switch (actionValue)
3485 {
3486 case "createElement":
3487 flags |= 0x00000001; // XMLFILE_CREATE_ELEMENT
3488 break;
3489 case "deleteValue":
3490 flags |= 0x00000002; // XMLFILE_DELETE_VALUE
3491 break;
3492 case "bulkSetValue":
3493 flags |= 0x00000004; // XMLFILE_BULKWRITE_VALUE
3494 break;
3495 case "setValue":
3496 // no flag for set value since it's the default
3497 break;
3498 default:
3499 this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, "Action", actionValue, "createElement", "deleteValue", "setValue", "bulkSetValue"));
3500 break;
3501 }
3502 break;
3503 case "SelectionLanguage":
3504 string selectionLanguage = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
3505 switch (selectionLanguage)
3506 {
3507 case "XPath":
3508 flags |= 0x00000100; // XMLFILE_USE_XPATH
3509 break;
3510 case "XSLPattern":
3511 // no flag for since it's the default
3512 break;
3513 default:
3514 this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, "SelectionLanguage", selectionLanguage, "XPath", "XSLPattern"));
3515 break;
3516 }
3517 break;
3518 case "Id":
3519 id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib);
3520 break;
3521 case "File":
3522 file = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
3523 break;
3524 case "ElementPath":
3525 elementPath = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
3526 break;
3527 case "Name":
3528 name = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
3529 break;
3530 case "Permanent":
3531 if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib))
3532 {
3533 flags |= 0x00010000; // XMLFILE_DONT_UNINSTALL
3534 }
3535 break;
3536 case "Sequence":
3537 sequence = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 1, Int16.MaxValue);
3538 break;
3539 case "Value":
3540 value = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
3541 break;
3542 case "PreserveModifiedDate":
3543 if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib))
3544 {
3545 flags |= 0x00001000; // XMLFILE_PRESERVE_MODIFIED
3546 }
3547 break;
3548 default:
3549 this.ParseHelper.UnexpectedAttribute(element, attrib);
3550 break;
3551 }
3552 }
3553 else
3554 {
3555 this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib);
3556 }
3557 }
3558
3559 if (null == id)
3560 {
3561 id = this.ParseHelper.CreateIdentifier("uxf", componentId, file, elementPath, name);
3562 }
3563
3564 if (null == file)
3565 {
3566 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "File"));
3567 }
3568
3569 if (null == elementPath)
3570 {
3571 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "ElementPath"));
3572 }
3573
3574 if ((0x00000001 /*XMLFILE_CREATE_ELEMENT*/ & flags) != 0 && null == name)
3575 {
3576 this.Messaging.Write(ErrorMessages.IllegalAttributeWithoutOtherAttributes(sourceLineNumbers, element.Name.LocalName, "Action", "Name"));
3577 }
3578
3579 this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element);
3580
3581 if (!this.Messaging.EncounteredError)
3582 {
3583 var symbol = section.AddSymbol(new XmlFileSymbol(sourceLineNumbers, id)
3584 {
3585 File = file,
3586 ElementPath = elementPath,
3587 Name = name,
3588 Value = value,
3589 Flags = flags,
3590 ComponentRef = componentId,
3591 });
3592 if (-1 != sequence)
3593 {
3594 symbol.Sequence = sequence;
3595 }
3596 }
3597
3598 this.AddReferenceToSchedXmlFile(sourceLineNumbers, section);
3599 }
3600
3601 /// <summary>
3602 /// Parses a XmlConfig element.
3603 /// </summary>
3604 /// <param name="element">Element to parse.</param>
3605 /// <param name="componentId">Identifier of parent component.</param>
3606 /// <param name="nested">Whether or not the element is nested.</param>
3607 private void ParseXmlConfigElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId, bool nested)
3608 {
3609 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element);
3610 Identifier id = null;
3611 string elementId = null;
3612 string elementPath = null;
3613 int flags = 0;
3614 string file = null;
3615 string name = null;
3616 var sequence = CompilerConstants.IntegerNotSet;
3617 string value = null;
3618 string verifyPath = null;
3619
3620 foreach (var attrib in element.Attributes())
3621 {
3622 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
3623 {
3624 switch (attrib.Name.LocalName)
3625 {
3626 case "Id":
3627 id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib);
3628 break;
3629 case "Action":
3630 if (nested)
3631 {
3632 this.Messaging.Write(ErrorMessages.IllegalAttributeWhenNested(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName, element.Parent.Name.LocalName));
3633 }
3634 else
3635 {
3636 string actionValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
3637 switch (actionValue)
3638 {
3639 case "create":
3640 flags |= 0x10; // XMLCONFIG_CREATE
3641 break;
3642 case "delete":
3643 flags |= 0x20; // XMLCONFIG_DELETE
3644 break;
3645 default:
3646 this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName, actionValue, "create", "delete"));
3647 break;
3648 }
3649 }
3650 break;
3651 case "ElementId":
3652 elementId = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
3653 break;
3654 case "ElementPath":
3655 elementPath = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
3656 break;
3657 case "File":
3658 file = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
3659 break;
3660 case "Name":
3661 name = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
3662 break;
3663 case "Node":
3664 if (nested)
3665 {
3666 this.Messaging.Write(ErrorMessages.IllegalAttributeWhenNested(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName, element.Parent.Name.LocalName));
3667 }
3668 else
3669 {
3670 var nodeValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
3671 switch (nodeValue)
3672 {
3673 case "element":
3674 flags |= 0x1; // XMLCONFIG_ELEMENT
3675 break;
3676 case "value":
3677 flags |= 0x2; // XMLCONFIG_VALUE
3678 break;
3679 case "document":
3680 flags |= 0x4; // XMLCONFIG_DOCUMENT
3681 break;
3682 default:
3683 this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName, nodeValue, "element", "value", "document"));
3684 break;
3685 }
3686 }
3687 break;
3688 case "On":
3689 if (nested)
3690 {
3691 this.Messaging.Write(ErrorMessages.IllegalAttributeWhenNested(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName, element.Parent.Name.LocalName));
3692 }
3693 else
3694 {
3695 var onValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
3696 switch (onValue)
3697 {
3698 case "install":
3699 flags |= 0x100; // XMLCONFIG_INSTALL
3700 break;
3701 case "uninstall":
3702 flags |= 0x200; // XMLCONFIG_UNINSTALL
3703 break;
3704 default:
3705 this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName, onValue, "install", "uninstall"));
3706 break;
3707 }
3708 }
3709 break;
3710 case "PreserveModifiedDate":
3711 if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib))
3712 {
3713 flags |= 0x00001000; // XMLCONFIG_PRESERVE_MODIFIED
3714 }
3715 break;
3716 case "Sequence":
3717 sequence = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 1, Int16.MaxValue);
3718 break;
3719 case "Value":
3720 value = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
3721 break;
3722 case "VerifyPath":
3723 verifyPath = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
3724 break;
3725 default:
3726 this.ParseHelper.UnexpectedAttribute(element, attrib);
3727 break;
3728 }
3729 }
3730 else
3731 {
3732 this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib);
3733 }
3734 }
3735
3736 if (null == id)
3737 {
3738 id = this.ParseHelper.CreateIdentifier("uxc", componentId, file, elementId, elementPath);
3739 }
3740
3741 if (null == file)
3742 {
3743 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "File"));
3744 }
3745
3746 if (null == elementId && null == elementPath)
3747 {
3748 this.Messaging.Write(ErrorMessages.ExpectedAttributes(sourceLineNumbers, element.Name.LocalName, "ElementId", "ElementPath"));
3749 }
3750 else if (null != elementId)
3751 {
3752 if (null != elementPath)
3753 {
3754 this.Messaging.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, element.Name.LocalName, "ElementId", "ElementPath"));
3755 }
3756
3757 if (0 != flags)
3758 {
3759 this.Messaging.Write(ErrorMessages.IllegalAttributeWithOtherAttributes(sourceLineNumbers, element.Name.LocalName, "ElementId", "Action", "Node", "On"));
3760 }
3761
3762 this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, UtilSymbolDefinitions.XmlConfig, elementId);
3763 }
3764
3765 // find unexpected child elements
3766 foreach (var child in element.Elements())
3767 {
3768 if (this.Namespace == child.Name.Namespace)
3769 {
3770 switch (child.Name.LocalName)
3771 {
3772 case "XmlConfig":
3773 if (nested)
3774 {
3775 this.Messaging.Write(ErrorMessages.UnexpectedElement(sourceLineNumbers, element.Name.LocalName, child.Name.LocalName));
3776 }
3777 else
3778 {
3779 this.ParseXmlConfigElement(intermediate, section, child, componentId, true);
3780 }
3781 break;
3782 default:
3783 this.ParseHelper.UnexpectedElement(element, child);
3784 break;
3785 }
3786 }
3787 else
3788 {
3789 this.ParseHelper.ParseExtensionElement(this.Context.Extensions, intermediate, section, element, child);
3790 }
3791 }
3792
3793 if (!this.Messaging.EncounteredError)
3794 {
3795 var symbol = section.AddSymbol(new XmlConfigSymbol(sourceLineNumbers, id)
3796 {
3797 File = file,
3798 ElementId = elementId,
3799 ElementPath = elementPath,
3800 VerifyPath = verifyPath,
3801 Name = name,
3802 Value = value,
3803 Flags = flags,
3804 ComponentRef = componentId,
3805 });
3806
3807 if (CompilerConstants.IntegerNotSet != sequence)
3808 {
3809 symbol.Sequence = sequence;
3810 }
3811 }
3812
3813 this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4SchedXmlConfig", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64);
3814 }
3815
3816 /// <summary>
3817 /// Match evaluator to escape properties in a string.
3818 /// </summary>
3819 private string EscapeProperties(Match match)
3820 {
3821 string escape = null;
3822 switch (match.Value)
3823 {
3824 case "[":
3825 escape = @"[\[]";
3826 break;
3827 case "]":
3828 escape = @"[\]]";
3829 break;
3830 }
3831
3832 return escape;
3833 }
3834
3835 private int CreateIntegerFromBitArray(BitArray bits)
3836 {
3837 if (32 != bits.Length)
3838 {
3839 throw new ArgumentException(String.Format("Can only convert a bit array with 32-bits to integer. Actual number of bits in array: {0}", bits.Length), "bits");
3840 }
3841
3842 var intArray = new int[1];
3843 bits.CopyTo(intArray, 0);
3844
3845 return intArray[0];
3846 }
3847
3848 private bool TrySetBitFromName(string[] attributeNames, string attributeName, YesNoType attributeValue, BitArray bits, int offset)
3849 {
3850 for (var i = 0; i < attributeNames.Length; i++)
3851 {
3852 if (attributeName.Equals(attributeNames[i], StringComparison.Ordinal))
3853 {
3854 bits.Set(i + offset, YesNoType.Yes == attributeValue);
3855 return true;
3856 }
3857 }
3858
3859 return false;
3860 }
3861
3862 private void AddReferenceToSchedXmlFile(SourceLineNumber sourceLineNumbers, IntermediateSection section)
3863 {
3864 this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4SchedXmlFile", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64);
3865 }
3866
3867 /// <summary>
3868 /// Private class that stores the data from a parsed PerformanceCounter element.
3869 /// </summary>
3870 private class ParsedPerformanceCounter
3871 {
3872 internal ParsedPerformanceCounter(string name, string help, System.Diagnostics.PerformanceCounterType type, int language)
3873 {
3874 this.Name = name;
3875 this.Help = help;
3876 this.Type = (int)type;
3877 this.Language = language.ToString("D3", CultureInfo.InvariantCulture);
3878 }
3879
3880 internal string Name { get; }
3881
3882 internal string Help { get; }
3883
3884 internal int Type { get; }
3885
3886 internal string Language { get; }
3887 }
3888 }
3889}
diff --git a/src/ext/Util/wixext/UtilConstants.cs b/src/ext/Util/wixext/UtilConstants.cs
new file mode 100644
index 00000000..28ff368f
--- /dev/null
+++ b/src/ext/Util/wixext/UtilConstants.cs
@@ -0,0 +1,17 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Util
4{
5 /// <summary>
6 /// Constants used by Utility Extension.
7 /// </summary>
8 internal static class UtilConstants
9 {
10 internal static readonly string[] FilePermissions = { "Read", "Write", "Append", "ReadExtendedAttributes", "WriteExtendedAttributes", "Execute", null, "ReadAttributes", "WriteAttributes" };
11 internal static readonly string[] FolderPermissions = { "Read", "CreateFile", "CreateChild", "ReadExtendedAttributes", "WriteExtendedAttributes", "Traverse", "DeleteChild", "ReadAttributes", "WriteAttributes" };
12 internal static readonly string[] GenericPermissions = { "GenericAll", "GenericExecute", "GenericWrite", "GenericRead" };
13 internal static readonly string[] RegistryPermissions = { "Read", "Write", "CreateSubkeys", "EnumerateSubkeys", "Notify", "CreateLink" };
14 internal static readonly string[] ServicePermissions = { "ServiceQueryConfig", "ServiceChangeConfig", "ServiceQueryStatus", "ServiceEnumerateDependents", "ServiceStart", "ServiceStop", "ServicePauseContinue", "ServiceInterrogate", "ServiceUserDefinedControl" };
15 internal static readonly string[] StandardPermissions = { "Delete", "ReadPermission", "ChangePermission", "TakeOwnership", "Synchronize" };
16 }
17}
diff --git a/src/ext/Util/wixext/UtilDecompiler.cs b/src/ext/Util/wixext/UtilDecompiler.cs
new file mode 100644
index 00000000..9ef3390f
--- /dev/null
+++ b/src/ext/Util/wixext/UtilDecompiler.cs
@@ -0,0 +1,1543 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Extensions
4{
5#if TODO_CONSIDER_DECOMPILER
6 using System;
7 using System.IO;
8 using System.Text;
9 using System.Collections;
10 using System.Diagnostics;
11 using System.Globalization;
12
13 using Util = WixToolset.Extensions.Serialize.Util;
14 using WixToolset.Data;
15 using WixToolset.Extensibility;
16 using Wix = WixToolset.Data.Serialize;
17
18 /// <summary>
19 /// The decompiler for the WiX Toolset Utility Extension.
20 /// </summary>
21 public sealed class UtilDecompiler : DecompilerExtension
22 {
23 /// <summary>
24 /// Creates a decompiler for Utility Extension.
25 /// </summary>
26 public UtilDecompiler()
27 {
28 this.TableDefinitions = UtilExtensionData.GetExtensionTableDefinitions();
29 }
30
31 /// <summary>
32 /// Get the extensions library to be removed.
33 /// </summary>
34 /// <param name="tableDefinitions">Table definitions for library.</param>
35 /// <returns>Library to remove from decompiled output.</returns>
36 public override Library GetLibraryToRemove(TableDefinitionCollection tableDefinitions)
37 {
38 return UtilExtensionData.GetExtensionLibrary(tableDefinitions);
39 }
40
41 /// <summary>
42 /// Called at the beginning of the decompilation of a database.
43 /// </summary>
44 /// <param name="tables">The collection of all tables.</param>
45 public override void Initialize(TableIndexedCollection tables)
46 {
47 this.CleanupSecureCustomProperties(tables);
48 this.CleanupInternetShortcutRemoveFileTables(tables);
49 }
50
51 /// <summary>
52 /// Decompile the SecureCustomProperties field to PropertyRefs for known extension properties.
53 /// </summary>
54 /// <remarks>
55 /// If we've referenced any of the suite or directory properties, add
56 /// a PropertyRef to refer to the Property (and associated custom action)
57 /// from the extension's library. Then remove the property from
58 /// SecureCustomExtensions property so later decompilation won't create
59 /// new Property elements.
60 /// </remarks>
61 /// <param name="tables">The collection of all tables.</param>
62 private void CleanupSecureCustomProperties(TableIndexedCollection tables)
63 {
64 Table propertyTable = tables["Property"];
65
66 if (null != propertyTable)
67 {
68 foreach (Row row in propertyTable.Rows)
69 {
70 if ("SecureCustomProperties" == row[0].ToString())
71 {
72 StringBuilder remainingProperties = new StringBuilder();
73 string[] secureCustomProperties = row[1].ToString().Split(';');
74 foreach (string property in secureCustomProperties)
75 {
76 if (property.StartsWith("WIX_SUITE_", StringComparison.Ordinal) || property.StartsWith("WIX_DIR_", StringComparison.Ordinal)
77 || property.StartsWith("WIX_ACCOUNT_", StringComparison.Ordinal))
78 {
79 Wix.PropertyRef propertyRef = new Wix.PropertyRef();
80 propertyRef.Id = property;
81 this.Core.RootElement.AddChild(propertyRef);
82 }
83 else
84 {
85 if (0 < remainingProperties.Length)
86 {
87 remainingProperties.Append(";");
88 }
89 remainingProperties.Append(property);
90 }
91 }
92
93 row[1] = remainingProperties.ToString();
94 break;
95 }
96 }
97 }
98 }
99
100 /// <summary>
101 /// Remove RemoveFile rows that the InternetShortcut compiler extension adds for us.
102 /// </summary>
103 /// <param name="tables">The collection of all tables.</param>
104 private void CleanupInternetShortcutRemoveFileTables(TableIndexedCollection tables)
105 {
106 // index the WixInternetShortcut table
107 Table wixInternetShortcutTable = tables["WixInternetShortcut"];
108 Hashtable wixInternetShortcuts = new Hashtable();
109 if (null != wixInternetShortcutTable)
110 {
111 foreach (Row row in wixInternetShortcutTable.Rows)
112 {
113 wixInternetShortcuts.Add(row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), row);
114 }
115 }
116
117 // remove the RemoveFile rows with primary keys that match the WixInternetShortcut table's
118 Table removeFileTable = tables["RemoveFile"];
119 if (null != removeFileTable)
120 {
121 for (int i = removeFileTable.Rows.Count - 1; 0 <= i; i--)
122 {
123 if (null != wixInternetShortcuts[removeFileTable.Rows[i][0]])
124 {
125 removeFileTable.Rows.RemoveAt(i);
126 }
127 }
128 }
129 }
130
131 /// <summary>
132 /// Decompiles an extension table.
133 /// </summary>
134 /// <param name="table">The table to decompile.</param>
135 public override void DecompileTable(Table table)
136 {
137 switch (table.Name)
138 {
139 case "WixCloseApplication":
140 this.DecompileWixCloseApplicationTable(table);
141 break;
142 case "WixRemoveFolderEx":
143 this.DecompileWixRemoveFolderExTable(table);
144 break;
145 case "WixRestartResource":
146 this.DecompileWixRestartResourceTable(table);
147 break;
148 case "FileShare":
149 this.DecompileFileShareTable(table);
150 break;
151 case "FileSharePermissions":
152 this.DecompileFileSharePermissionsTable(table);
153 break;
154 case "WixInternetShortcut":
155 this.DecompileWixInternetShortcutTable(table);
156 break;
157 case "Group":
158 this.DecompileGroupTable(table);
159 break;
160 case "Perfmon":
161 this.DecompilePerfmonTable(table);
162 break;
163 case "PerfmonManifest":
164 this.DecompilePerfmonManifestTable(table);
165 break;
166 case "EventManifest":
167 this.DecompileEventManifestTable(table);
168 break;
169 case "SecureObjects":
170 this.DecompileSecureObjectsTable(table);
171 break;
172 case "ServiceConfig":
173 this.DecompileServiceConfigTable(table);
174 break;
175 case "User":
176 this.DecompileUserTable(table);
177 break;
178 case "UserGroup":
179 this.DecompileUserGroupTable(table);
180 break;
181 case "XmlConfig":
182 this.DecompileXmlConfigTable(table);
183 break;
184 case "XmlFile":
185 // XmlFile decompilation has been moved to FinalizeXmlFileTable function
186 break;
187 default:
188 base.DecompileTable(table);
189 break;
190 }
191 }
192
193 /// <summary>
194 /// Finalize decompilation.
195 /// </summary>
196 /// <param name="tables">The collection of all tables.</param>
197 public override void Finish(TableIndexedCollection tables)
198 {
199 this.FinalizePerfmonTable(tables);
200 this.FinalizePerfmonManifestTable(tables);
201 this.FinalizeSecureObjectsTable(tables);
202 this.FinalizeServiceConfigTable(tables);
203 this.FinalizeXmlConfigTable(tables);
204 this.FinalizeXmlFileTable(tables);
205 this.FinalizeEventManifestTable(tables);
206 }
207
208 /// <summary>
209 /// Decompile the WixCloseApplication table.
210 /// </summary>
211 /// <param name="table">The table to decompile.</param>
212 private void DecompileWixCloseApplicationTable(Table table)
213 {
214 foreach (Row row in table.Rows)
215 {
216 Util.CloseApplication closeApplication = new Util.CloseApplication();
217
218 closeApplication.Id = (string)row[0];
219
220 closeApplication.Target = (string)row[1];
221
222 if (null != row[2])
223 {
224 closeApplication.Description = (string)row[2];
225 }
226
227 if (null != row[3])
228 {
229 closeApplication.Content = (string)row[3];
230 }
231
232 // set defaults
233 closeApplication.CloseMessage = Util.YesNoType.no;
234 closeApplication.RebootPrompt = Util.YesNoType.yes;
235 closeApplication.ElevatedCloseMessage = Util.YesNoType.no;
236
237 if (null != row[4])
238 {
239 int attribute = (int)row[4];
240
241 closeApplication.CloseMessage = (0x1 == (attribute & 0x1)) ? Util.YesNoType.yes : Util.YesNoType.no;
242 closeApplication.RebootPrompt = (0x2 == (attribute & 0x2)) ? Util.YesNoType.yes : Util.YesNoType.no;
243 closeApplication.ElevatedCloseMessage = (0x4 == (attribute & 0x4)) ? Util.YesNoType.yes : Util.YesNoType.no;
244 }
245
246 if (null != row[5])
247 {
248 closeApplication.Sequence = (int)row[5];
249 }
250
251 if (null != row[6])
252 {
253 closeApplication.Property = (string)row[6];
254 }
255
256 this.Core.RootElement.AddChild(closeApplication);
257 }
258 }
259
260 /// <summary>
261 /// Decompile the WixRemoveFolderEx table.
262 /// </summary>
263 /// <param name="table">The table to decompile.</param>
264 private void DecompileWixRemoveFolderExTable(Table table)
265 {
266 foreach (Row row in table.Rows)
267 {
268 // Set the Id even if auto-generated previously.
269 Util.RemoveFolderEx removeFolder = new Util.RemoveFolderEx();
270 removeFolder.Id = (string)row[0];
271 removeFolder.Property = (string)row[2];
272
273 int installMode = (int)row[3];
274 switch ((UtilCompiler.WixRemoveFolderExOn)installMode)
275 {
276 case UtilCompiler.WixRemoveFolderExOn.Install:
277 removeFolder.On = Util.RemoveFolderEx.OnType.install;
278 break;
279
280 case UtilCompiler.WixRemoveFolderExOn.Uninstall:
281 removeFolder.On = Util.RemoveFolderEx.OnType.uninstall;
282 break;
283
284 case UtilCompiler.WixRemoveFolderExOn.Both:
285 removeFolder.On = Util.RemoveFolderEx.OnType.both;
286 break;
287
288 default:
289 this.Core.OnMessage(WixWarnings.UnrepresentableColumnValue(row.SourceLineNumbers, table.Name, "InstallMode", installMode));
290 break;
291 }
292
293 // Add to the appropriate Component or section element.
294 string componentId = (string)row[1];
295 Wix.Component component = (Wix.Component)this.Core.GetIndexedElement("Component", componentId);
296 if (null != component)
297 {
298 component.AddChild(removeFolder);
299 }
300 else
301 {
302 this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Component_", componentId, "Component"));
303 }
304 }
305 }
306
307 /// <summary>
308 /// Decompile the WixRestartResource table.
309 /// </summary>
310 /// <param name="table">The table to decompile.</param>
311 private void DecompileWixRestartResourceTable(Table table)
312 {
313 foreach (Row row in table.Rows)
314 {
315 // Set the Id even if auto-generated previously.
316 Util.RestartResource restartResource = new Util.RestartResource();
317 restartResource.Id = (string)row[0];
318
319 // Determine the resource type and set accordingly.
320 string resource = (string)row[2];
321 int attributes = (int)row[3];
322 UtilCompiler.WixRestartResourceAttributes type = (UtilCompiler.WixRestartResourceAttributes)(attributes & (int)UtilCompiler.WixRestartResourceAttributes.TypeMask);
323
324 switch (type)
325 {
326 case UtilCompiler.WixRestartResourceAttributes.Filename:
327 restartResource.Path = resource;
328 break;
329
330 case UtilCompiler.WixRestartResourceAttributes.ProcessName:
331 restartResource.ProcessName = resource;
332 break;
333
334 case UtilCompiler.WixRestartResourceAttributes.ServiceName:
335 restartResource.ServiceName = resource;
336 break;
337
338 default:
339 this.Core.OnMessage(WixWarnings.UnrepresentableColumnValue(row.SourceLineNumbers, table.Name, "Attributes", attributes));
340 break;
341 }
342
343 // Add to the appropriate Component or section element.
344 string componentId = (string)row[1];
345 if (!String.IsNullOrEmpty(componentId))
346 {
347 Wix.Component component = (Wix.Component)this.Core.GetIndexedElement("Component", componentId);
348 if (null != component)
349 {
350 component.AddChild(restartResource);
351 }
352 else
353 {
354 this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Component_", componentId, "Component"));
355 }
356 }
357 else
358 {
359 this.Core.RootElement.AddChild(restartResource);
360 }
361 }
362 }
363
364 /// <summary>
365 /// Decompile the FileShare table.
366 /// </summary>
367 /// <param name="table">The table to decompile.</param>
368 private void DecompileFileShareTable(Table table)
369 {
370 foreach (Row row in table.Rows)
371 {
372 Util.FileShare fileShare = new Util.FileShare();
373
374 fileShare.Id = (string)row[0];
375
376 fileShare.Name = (string)row[1];
377
378 if (null != row[3])
379 {
380 fileShare.Description = (string)row[3];
381 }
382
383 // the Directory_ column is set by the parent Component
384
385 // the User_ and Permissions columns are deprecated
386
387 Wix.Component component = (Wix.Component)this.Core.GetIndexedElement("Component", (string)row[2]);
388 if (null != component)
389 {
390 component.AddChild(fileShare);
391 }
392 else
393 {
394 this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Component_", (string)row[2], "Component"));
395 }
396 this.Core.IndexElement(row, fileShare);
397 }
398 }
399
400 /// <summary>
401 /// Decompile the FileSharePermissions table.
402 /// </summary>
403 /// <param name="table">The table to decompile.</param>
404 private void DecompileFileSharePermissionsTable(Table table)
405 {
406 foreach (Row row in table.Rows)
407 {
408 Util.FileSharePermission fileSharePermission = new Util.FileSharePermission();
409
410 fileSharePermission.User = (string)row[1];
411
412 string[] specialPermissions = UtilConstants.FolderPermissions;
413 int permissions = (int)row[2];
414 for (int i = 0; i < 32; i++)
415 {
416 if (0 != ((permissions >> i) & 1))
417 {
418 string name = null;
419
420 if (16 > i && specialPermissions.Length > i)
421 {
422 name = specialPermissions[i];
423 }
424 else if (28 > i && UtilConstants.StandardPermissions.Length > (i - 16))
425 {
426 name = UtilConstants.StandardPermissions[i - 16];
427 }
428 else if (0 <= (i - 28) && UtilConstants.GenericPermissions.Length > (i - 28))
429 {
430 name = UtilConstants.GenericPermissions[i - 28];
431 }
432
433 if (null == name)
434 {
435 this.Core.OnMessage(WixWarnings.UnknownPermission(row.SourceLineNumbers, row.Table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), i));
436 }
437 else
438 {
439 switch (name)
440 {
441 case "ChangePermission":
442 fileSharePermission.ChangePermission = Util.YesNoType.yes;
443 break;
444 case "CreateChild":
445 fileSharePermission.CreateChild = Util.YesNoType.yes;
446 break;
447 case "CreateFile":
448 fileSharePermission.CreateFile = Util.YesNoType.yes;
449 break;
450 case "Delete":
451 fileSharePermission.Delete = Util.YesNoType.yes;
452 break;
453 case "DeleteChild":
454 fileSharePermission.DeleteChild = Util.YesNoType.yes;
455 break;
456 case "GenericAll":
457 fileSharePermission.GenericAll = Util.YesNoType.yes;
458 break;
459 case "GenericExecute":
460 fileSharePermission.GenericExecute = Util.YesNoType.yes;
461 break;
462 case "GenericRead":
463 fileSharePermission.GenericRead = Util.YesNoType.yes;
464 break;
465 case "GenericWrite":
466 fileSharePermission.GenericWrite = Util.YesNoType.yes;
467 break;
468 case "Read":
469 fileSharePermission.Read = Util.YesNoType.yes;
470 break;
471 case "ReadAttributes":
472 fileSharePermission.ReadAttributes = Util.YesNoType.yes;
473 break;
474 case "ReadExtendedAttributes":
475 fileSharePermission.ReadExtendedAttributes = Util.YesNoType.yes;
476 break;
477 case "ReadPermission":
478 fileSharePermission.ReadPermission = Util.YesNoType.yes;
479 break;
480 case "Synchronize":
481 fileSharePermission.Synchronize = Util.YesNoType.yes;
482 break;
483 case "TakeOwnership":
484 fileSharePermission.TakeOwnership = Util.YesNoType.yes;
485 break;
486 case "Traverse":
487 fileSharePermission.Traverse = Util.YesNoType.yes;
488 break;
489 case "WriteAttributes":
490 fileSharePermission.WriteAttributes = Util.YesNoType.yes;
491 break;
492 case "WriteExtendedAttributes":
493 fileSharePermission.WriteExtendedAttributes = Util.YesNoType.yes;
494 break;
495 default:
496 Debug.Fail(String.Format("Unknown permission '{0}'.", name));
497 break;
498 }
499 }
500 }
501 }
502
503 Util.FileShare fileShare = (Util.FileShare)this.Core.GetIndexedElement("FileShare", (string)row[0]);
504 if (null != fileShare)
505 {
506 fileShare.AddChild(fileSharePermission);
507 }
508 else
509 {
510 this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "FileShare_", (string)row[0], "FileShare"));
511 }
512 }
513 }
514
515 /// <summary>
516 /// Decompile the Group table.
517 /// </summary>
518 /// <param name="table">The table to decompile.</param>
519 private void DecompileGroupTable(Table table)
520 {
521 foreach (Row row in table.Rows)
522 {
523 Util.Group group = new Util.Group();
524
525 group.Id = (string)row[0];
526
527 if (null != row[1])
528 {
529 this.Core.OnMessage(WixWarnings.UnrepresentableColumnValue(row.SourceLineNumbers, table.Name, "Component_", (string)row[1]));
530 }
531
532 group.Name = (string)row[2];
533
534 if (null != row[3])
535 {
536 group.Domain = (string)row[3];
537 }
538
539 this.Core.RootElement.AddChild(group);
540 }
541 }
542
543 /// <summary>
544 /// Decompile the WixInternetShortcut table.
545 /// </summary>
546 /// <param name="table">The table to decompile.</param>
547 private void DecompileWixInternetShortcutTable(Table table)
548 {
549 foreach (Row row in table.Rows)
550 {
551 Util.InternetShortcut internetShortcut = new Util.InternetShortcut();
552 internetShortcut.Id = (string)row[0];
553 internetShortcut.Directory = (string)row[2];
554 // remove .lnk/.url extension because compiler extension adds it back for us
555 internetShortcut.Name = Path.ChangeExtension((string)row[3], null);
556 internetShortcut.Target = (string)row[4];
557 internetShortcut.IconFile = (string)row[6];
558 internetShortcut.IconIndex = (int)row[7];
559
560 UtilCompiler.InternetShortcutType shortcutType = (UtilCompiler.InternetShortcutType)row[5];
561 switch (shortcutType)
562 {
563 case UtilCompiler.InternetShortcutType.Link:
564 internetShortcut.Type = Util.InternetShortcut.TypeType.link;
565 break;
566 case UtilCompiler.InternetShortcutType.Url:
567 internetShortcut.Type = Util.InternetShortcut.TypeType.url;
568 break;
569 }
570
571 Wix.Component component = (Wix.Component)this.Core.GetIndexedElement("Component", (string)row[1]);
572 if (null != component)
573 {
574 component.AddChild(internetShortcut);
575 }
576 else
577 {
578 this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Component_", (string)row[1], "Component"));
579 }
580
581 this.Core.IndexElement(row, internetShortcut);
582 }
583 }
584
585 /// <summary>
586 /// Decompile the Perfmon table.
587 /// </summary>
588 /// <param name="table">The table to decompile.</param>
589 private void DecompilePerfmonTable(Table table)
590 {
591 foreach (Row row in table.Rows)
592 {
593 Util.PerfCounter perfCounter = new Util.PerfCounter();
594
595 perfCounter.Name = (string)row[2];
596
597 this.Core.IndexElement(row, perfCounter);
598 }
599 }
600
601 /// <summary>
602 /// Decompile the PerfmonManifest table.
603 /// </summary>
604 /// <param name="table">The table to decompile.</param>
605 private void DecompilePerfmonManifestTable(Table table)
606 {
607 foreach (Row row in table.Rows)
608 {
609 Util.PerfCounterManifest perfCounterManifest = new Util.PerfCounterManifest();
610
611 perfCounterManifest.ResourceFileDirectory = (string)row[2];
612
613 this.Core.IndexElement(row, perfCounterManifest);
614 }
615 }
616
617 /// <summary>
618 /// Decompile the EventManifest table.
619 /// </summary>
620 /// <param name="table">The table to decompile.</param>
621 private void DecompileEventManifestTable(Table table)
622 {
623 foreach (Row row in table.Rows)
624 {
625 Util.EventManifest eventManifest = new Util.EventManifest();
626 this.Core.IndexElement(row, eventManifest);
627 }
628 }
629
630 /// <summary>
631 /// Decompile the SecureObjects table.
632 /// </summary>
633 /// <param name="table">The table to decompile.</param>
634 private void DecompileSecureObjectsTable(Table table)
635 {
636 foreach (Row row in table.Rows)
637 {
638 Util.PermissionEx permissionEx = new Util.PermissionEx();
639
640 string[] specialPermissions;
641 switch ((string)row[1])
642 {
643 case "CreateFolder":
644 specialPermissions = UtilConstants.FolderPermissions;
645 break;
646 case "File":
647 specialPermissions = UtilConstants.FilePermissions;
648 break;
649 case "Registry":
650 specialPermissions = UtilConstants.RegistryPermissions;
651 break;
652 case "ServiceInstall":
653 specialPermissions = UtilConstants.ServicePermissions;
654 break;
655 default:
656 this.Core.OnMessage(WixWarnings.IllegalColumnValue(row.SourceLineNumbers, row.Table.Name, row.Fields[1].Column.Name, row[1]));
657 return;
658 }
659
660 int permissionBits = (int)row[4];
661 for (int i = 0; i < 32; i++)
662 {
663 if (0 != ((permissionBits >> i) & 1))
664 {
665 string name = null;
666
667 if (16 > i && specialPermissions.Length > i)
668 {
669 name = specialPermissions[i];
670 }
671 else if (28 > i && UtilConstants.StandardPermissions.Length > (i - 16))
672 {
673 name = UtilConstants.StandardPermissions[i - 16];
674 }
675 else if (0 <= (i - 28) && UtilConstants.GenericPermissions.Length > (i - 28))
676 {
677 name = UtilConstants.GenericPermissions[i - 28];
678 }
679
680 if (null == name)
681 {
682 this.Core.OnMessage(WixWarnings.UnknownPermission(row.SourceLineNumbers, row.Table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), i));
683 }
684 else
685 {
686 switch (name)
687 {
688 case "Append":
689 permissionEx.Append = Util.YesNoType.yes;
690 break;
691 case "ChangePermission":
692 permissionEx.ChangePermission = Util.YesNoType.yes;
693 break;
694 case "CreateChild":
695 permissionEx.CreateChild = Util.YesNoType.yes;
696 break;
697 case "CreateFile":
698 permissionEx.CreateFile = Util.YesNoType.yes;
699 break;
700 case "CreateLink":
701 permissionEx.CreateLink = Util.YesNoType.yes;
702 break;
703 case "CreateSubkeys":
704 permissionEx.CreateSubkeys = Util.YesNoType.yes;
705 break;
706 case "Delete":
707 permissionEx.Delete = Util.YesNoType.yes;
708 break;
709 case "DeleteChild":
710 permissionEx.DeleteChild = Util.YesNoType.yes;
711 break;
712 case "EnumerateSubkeys":
713 permissionEx.EnumerateSubkeys = Util.YesNoType.yes;
714 break;
715 case "Execute":
716 permissionEx.Execute = Util.YesNoType.yes;
717 break;
718 case "GenericAll":
719 permissionEx.GenericAll = Util.YesNoType.yes;
720 break;
721 case "GenericExecute":
722 permissionEx.GenericExecute = Util.YesNoType.yes;
723 break;
724 case "GenericRead":
725 permissionEx.GenericRead = Util.YesNoType.yes;
726 break;
727 case "GenericWrite":
728 permissionEx.GenericWrite = Util.YesNoType.yes;
729 break;
730 case "Notify":
731 permissionEx.Notify = Util.YesNoType.yes;
732 break;
733 case "Read":
734 permissionEx.Read = Util.YesNoType.yes;
735 break;
736 case "ReadAttributes":
737 permissionEx.ReadAttributes = Util.YesNoType.yes;
738 break;
739 case "ReadExtendedAttributes":
740 permissionEx.ReadExtendedAttributes = Util.YesNoType.yes;
741 break;
742 case "ReadPermission":
743 permissionEx.ReadPermission = Util.YesNoType.yes;
744 break;
745 case "ServiceChangeConfig":
746 permissionEx.ServiceChangeConfig = Util.YesNoType.yes;
747 break;
748 case "ServiceEnumerateDependents":
749 permissionEx.ServiceEnumerateDependents = Util.YesNoType.yes;
750 break;
751 case "ServiceInterrogate":
752 permissionEx.ServiceInterrogate = Util.YesNoType.yes;
753 break;
754 case "ServicePauseContinue":
755 permissionEx.ServicePauseContinue = Util.YesNoType.yes;
756 break;
757 case "ServiceQueryConfig":
758 permissionEx.ServiceQueryConfig = Util.YesNoType.yes;
759 break;
760 case "ServiceQueryStatus":
761 permissionEx.ServiceQueryStatus = Util.YesNoType.yes;
762 break;
763 case "ServiceStart":
764 permissionEx.ServiceStart = Util.YesNoType.yes;
765 break;
766 case "ServiceStop":
767 permissionEx.ServiceStop = Util.YesNoType.yes;
768 break;
769 case "ServiceUserDefinedControl":
770 permissionEx.ServiceUserDefinedControl = Util.YesNoType.yes;
771 break;
772 case "Synchronize":
773 permissionEx.Synchronize = Util.YesNoType.yes;
774 break;
775 case "TakeOwnership":
776 permissionEx.TakeOwnership = Util.YesNoType.yes;
777 break;
778 case "Traverse":
779 permissionEx.Traverse = Util.YesNoType.yes;
780 break;
781 case "Write":
782 permissionEx.Write = Util.YesNoType.yes;
783 break;
784 case "WriteAttributes":
785 permissionEx.WriteAttributes = Util.YesNoType.yes;
786 break;
787 case "WriteExtendedAttributes":
788 permissionEx.WriteExtendedAttributes = Util.YesNoType.yes;
789 break;
790 default:
791 throw new InvalidOperationException(String.Format("Unknown permission attribute '{0}'.", name));
792 }
793 }
794 }
795 }
796
797 if (null != row[2])
798 {
799 permissionEx.Domain = (string)row[2];
800 }
801
802 permissionEx.User = (string)row[3];
803
804 this.Core.IndexElement(row, permissionEx);
805 }
806 }
807
808 /// <summary>
809 /// Decompile the ServiceConfig table.
810 /// </summary>
811 /// <param name="table">The table to decompile.</param>
812 private void DecompileServiceConfigTable(Table table)
813 {
814 foreach (Row row in table.Rows)
815 {
816 Util.ServiceConfig serviceConfig = new Util.ServiceConfig();
817
818 serviceConfig.ServiceName = (string)row[0];
819
820 switch ((string)row[3])
821 {
822 case "none":
823 serviceConfig.FirstFailureActionType = Util.ServiceConfig.FirstFailureActionTypeType.none;
824 break;
825 case "reboot":
826 serviceConfig.FirstFailureActionType = Util.ServiceConfig.FirstFailureActionTypeType.reboot;
827 break;
828 case "restart":
829 serviceConfig.FirstFailureActionType = Util.ServiceConfig.FirstFailureActionTypeType.restart;
830 break;
831 case "runCommand":
832 serviceConfig.FirstFailureActionType = Util.ServiceConfig.FirstFailureActionTypeType.runCommand;
833 break;
834 default:
835 this.Core.OnMessage(WixWarnings.IllegalColumnValue(row.SourceLineNumbers, table.Name, row.Fields[3].Column.Name, row[3]));
836 break;
837 }
838
839 switch ((string)row[4])
840 {
841 case "none":
842 serviceConfig.SecondFailureActionType = Util.ServiceConfig.SecondFailureActionTypeType.none;
843 break;
844 case "reboot":
845 serviceConfig.SecondFailureActionType = Util.ServiceConfig.SecondFailureActionTypeType.reboot;
846 break;
847 case "restart":
848 serviceConfig.SecondFailureActionType = Util.ServiceConfig.SecondFailureActionTypeType.restart;
849 break;
850 case "runCommand":
851 serviceConfig.SecondFailureActionType = Util.ServiceConfig.SecondFailureActionTypeType.runCommand;
852 break;
853 default:
854 this.Core.OnMessage(WixWarnings.IllegalColumnValue(row.SourceLineNumbers, table.Name, row.Fields[4].Column.Name, row[4]));
855 break;
856 }
857
858 switch ((string)row[5])
859 {
860 case "none":
861 serviceConfig.ThirdFailureActionType = Util.ServiceConfig.ThirdFailureActionTypeType.none;
862 break;
863 case "reboot":
864 serviceConfig.ThirdFailureActionType = Util.ServiceConfig.ThirdFailureActionTypeType.reboot;
865 break;
866 case "restart":
867 serviceConfig.ThirdFailureActionType = Util.ServiceConfig.ThirdFailureActionTypeType.restart;
868 break;
869 case "runCommand":
870 serviceConfig.ThirdFailureActionType = Util.ServiceConfig.ThirdFailureActionTypeType.runCommand;
871 break;
872 default:
873 this.Core.OnMessage(WixWarnings.IllegalColumnValue(row.SourceLineNumbers, table.Name, row.Fields[5].Column.Name, row[5]));
874 break;
875 }
876
877 if (null != row[6])
878 {
879 serviceConfig.ResetPeriodInDays = (int)row[6];
880 }
881
882 if (null != row[7])
883 {
884 serviceConfig.RestartServiceDelayInSeconds = (int)row[7];
885 }
886
887 if (null != row[8])
888 {
889 serviceConfig.ProgramCommandLine = (string)row[8];
890 }
891
892 if (null != row[9])
893 {
894 serviceConfig.RebootMessage = (string)row[9];
895 }
896
897 this.Core.IndexElement(row, serviceConfig);
898 }
899 }
900
901 /// <summary>
902 /// Decompile the User table.
903 /// </summary>
904 /// <param name="table">The table to decompile.</param>
905 private void DecompileUserTable(Table table)
906 {
907 foreach (Row row in table.Rows)
908 {
909 Util.User user = new Util.User();
910
911 user.Id = (string)row[0];
912
913 user.Name = (string)row[2];
914
915 if (null != row[3])
916 {
917 user.Domain = (string)row[3];
918 }
919
920 if (null != row[4])
921 {
922 user.Password = (string)row[4];
923 }
924
925 if (null != row[5])
926 {
927 int attributes = (int)row[5];
928
929 if (UtilCompiler.UserDontExpirePasswrd == (attributes & UtilCompiler.UserDontExpirePasswrd))
930 {
931 user.PasswordNeverExpires = Util.YesNoType.yes;
932 }
933
934 if (UtilCompiler.UserPasswdCantChange == (attributes & UtilCompiler.UserPasswdCantChange))
935 {
936 user.CanNotChangePassword = Util.YesNoType.yes;
937 }
938
939 if (UtilCompiler.UserPasswdChangeReqdOnLogin == (attributes & UtilCompiler.UserPasswdChangeReqdOnLogin))
940 {
941 user.PasswordExpired = Util.YesNoType.yes;
942 }
943
944 if (UtilCompiler.UserDisableAccount == (attributes & UtilCompiler.UserDisableAccount))
945 {
946 user.Disabled = Util.YesNoType.yes;
947 }
948
949 if (UtilCompiler.UserFailIfExists == (attributes & UtilCompiler.UserFailIfExists))
950 {
951 user.FailIfExists = Util.YesNoType.yes;
952 }
953
954 if (UtilCompiler.UserUpdateIfExists == (attributes & UtilCompiler.UserUpdateIfExists))
955 {
956 user.UpdateIfExists = Util.YesNoType.yes;
957 }
958
959 if (UtilCompiler.UserLogonAsService == (attributes & UtilCompiler.UserLogonAsService))
960 {
961 user.LogonAsService = Util.YesNoType.yes;
962 }
963
964 if (UtilCompiler.UserDontRemoveOnUninstall == (attributes & UtilCompiler.UserDontRemoveOnUninstall))
965 {
966 user.RemoveOnUninstall = Util.YesNoType.no;
967 }
968
969 if (UtilCompiler.UserDontCreateUser == (attributes & UtilCompiler.UserDontCreateUser))
970 {
971 user.CreateUser = Util.YesNoType.no;
972 }
973
974 if (UtilCompiler.UserNonVital == (attributes & UtilCompiler.UserNonVital))
975 {
976 user.Vital = Util.YesNoType.no;
977 }
978 }
979
980 if (null != row[1])
981 {
982 Wix.Component component = (Wix.Component)this.Core.GetIndexedElement("Component", (string)row[1]);
983
984 if (null != component)
985 {
986 component.AddChild(user);
987 }
988 else
989 {
990 this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Component_", (string)row[1], "Component"));
991 }
992 }
993 else
994 {
995 this.Core.RootElement.AddChild(user);
996 }
997 this.Core.IndexElement(row, user);
998 }
999 }
1000
1001 /// <summary>
1002 /// Decompile the UserGroup table.
1003 /// </summary>
1004 /// <param name="table">The table to decompile.</param>
1005 private void DecompileUserGroupTable(Table table)
1006 {
1007 foreach (Row row in table.Rows)
1008 {
1009 Util.User user = (Util.User)this.Core.GetIndexedElement("User", (string)row[0]);
1010
1011 if (null != user)
1012 {
1013 Util.GroupRef groupRef = new Util.GroupRef();
1014
1015 groupRef.Id = (string)row[1];
1016
1017 user.AddChild(groupRef);
1018 }
1019 else
1020 {
1021 this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Group_", (string)row[0], "Group"));
1022 }
1023 }
1024 }
1025
1026 /// <summary>
1027 /// Decompile the XmlConfig table.
1028 /// </summary>
1029 /// <param name="table">The table to decompile.</param>
1030 private void DecompileXmlConfigTable(Table table)
1031 {
1032 foreach (Row row in table.Rows)
1033 {
1034 Util.XmlConfig xmlConfig = new Util.XmlConfig();
1035
1036 xmlConfig.Id = (string)row[0];
1037
1038 xmlConfig.File = (string)row[1];
1039
1040 xmlConfig.ElementPath = (string)row[2];
1041
1042 if (null != row[3])
1043 {
1044 xmlConfig.VerifyPath = (string)row[3];
1045 }
1046
1047 if (null != row[4])
1048 {
1049 xmlConfig.Name = (string)row[4];
1050 }
1051
1052 if (null != row[5])
1053 {
1054 xmlConfig.Value = (string)row[5];
1055 }
1056
1057 int flags = (int)row[6];
1058
1059 if (0x1 == (flags & 0x1))
1060 {
1061 xmlConfig.Node = Util.XmlConfig.NodeType.element;
1062 }
1063 else if (0x2 == (flags & 0x2))
1064 {
1065 xmlConfig.Node = Util.XmlConfig.NodeType.value;
1066 }
1067 else if (0x4 == (flags & 0x4))
1068 {
1069 xmlConfig.Node = Util.XmlConfig.NodeType.document;
1070 }
1071
1072 if (0x10 == (flags & 0x10))
1073 {
1074 xmlConfig.Action = Util.XmlConfig.ActionType.create;
1075 }
1076 else if (0x20 == (flags & 0x20))
1077 {
1078 xmlConfig.Action = Util.XmlConfig.ActionType.delete;
1079 }
1080
1081 if (0x100 == (flags & 0x100))
1082 {
1083 xmlConfig.On = Util.XmlConfig.OnType.install;
1084 }
1085 else if (0x200 == (flags & 0x200))
1086 {
1087 xmlConfig.On = Util.XmlConfig.OnType.uninstall;
1088 }
1089
1090 if (0x00001000 == (flags & 0x00001000))
1091 {
1092 xmlConfig.PreserveModifiedDate = Util.YesNoType.yes;
1093 }
1094
1095 if (null != row[8])
1096 {
1097 xmlConfig.Sequence = (int)row[8];
1098 }
1099
1100 this.Core.IndexElement(row, xmlConfig);
1101 }
1102 }
1103
1104 /// <summary>
1105 /// Finalize the Perfmon table.
1106 /// </summary>
1107 /// <param name="tables">The collection of all tables.</param>
1108 /// <remarks>
1109 /// Since the PerfCounter element nests under a File element, but
1110 /// the Perfmon table does not have a foreign key relationship with
1111 /// the File table (instead it has a formatted string that usually
1112 /// refers to a file row - but doesn't have to), the nesting must
1113 /// be inferred during finalization.
1114 /// </remarks>
1115 private void FinalizePerfmonTable(TableIndexedCollection tables)
1116 {
1117 Table perfmonTable = tables["Perfmon"];
1118
1119 if (null != perfmonTable)
1120 {
1121 foreach (Row row in perfmonTable.Rows)
1122 {
1123 string formattedFile = (string)row[1];
1124 Util.PerfCounter perfCounter = (Util.PerfCounter)this.Core.GetIndexedElement(row);
1125
1126 // try to "de-format" the File column's value to determine the proper parent File element
1127 if ((formattedFile.StartsWith("[#", StringComparison.Ordinal) || formattedFile.StartsWith("[!", StringComparison.Ordinal))
1128 && formattedFile.EndsWith("]", StringComparison.Ordinal))
1129 {
1130 string fileId = formattedFile.Substring(2, formattedFile.Length - 3);
1131
1132 Wix.File file = (Wix.File)this.Core.GetIndexedElement("File", fileId);
1133 if (null != file)
1134 {
1135 file.AddChild(perfCounter);
1136 }
1137 else
1138 {
1139 this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, perfmonTable.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "File", formattedFile, "File"));
1140 }
1141 }
1142 else
1143 {
1144 this.Core.OnMessage(UtilErrors.IllegalFileValueInPerfmonOrManifest(formattedFile, "Perfmon"));
1145 }
1146 }
1147 }
1148 }
1149
1150 /// <summary>
1151 /// Finalize the PerfmonManifest table.
1152 /// </summary>
1153 /// <param name="tables">The collection of all tables.</param>
1154 private void FinalizePerfmonManifestTable(TableIndexedCollection tables)
1155 {
1156 Table perfmonManifestTable = tables["PerfmonManifest"];
1157
1158 if (null != perfmonManifestTable)
1159 {
1160 foreach (Row row in perfmonManifestTable.Rows)
1161 {
1162 string formattedFile = (string)row[1];
1163 Util.PerfCounterManifest perfCounterManifest = (Util.PerfCounterManifest)this.Core.GetIndexedElement(row);
1164
1165 // try to "de-format" the File column's value to determine the proper parent File element
1166 if ((formattedFile.StartsWith("[#", StringComparison.Ordinal) || formattedFile.StartsWith("[!", StringComparison.Ordinal))
1167 && formattedFile.EndsWith("]", StringComparison.Ordinal))
1168 {
1169 string fileId = formattedFile.Substring(2, formattedFile.Length - 3);
1170
1171 Wix.File file = (Wix.File)this.Core.GetIndexedElement("File", fileId);
1172 if (null != file)
1173 {
1174 file.AddChild(perfCounterManifest);
1175 }
1176 else
1177 {
1178 this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, perfCounterManifest.ResourceFileDirectory, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "File", formattedFile, "File"));
1179 }
1180 }
1181 else
1182 {
1183 this.Core.OnMessage(UtilErrors.IllegalFileValueInPerfmonOrManifest(formattedFile, "PerfmonManifest"));
1184 }
1185 }
1186 }
1187 }
1188
1189 /// <summary>
1190 /// Finalize the SecureObjects table.
1191 /// </summary>
1192 /// <param name="tables">The collection of all tables.</param>
1193 /// <remarks>
1194 /// Nests the PermissionEx elements below their parent elements. There are no declared foreign
1195 /// keys for the parents of the SecureObjects table.
1196 /// </remarks>
1197 private void FinalizeSecureObjectsTable(TableIndexedCollection tables)
1198 {
1199 Table createFolderTable = tables["CreateFolder"];
1200 Table secureObjectsTable = tables["SecureObjects"];
1201
1202 Hashtable createFolders = new Hashtable();
1203
1204 // index the CreateFolder table because the foreign key to this table from the
1205 // LockPermissions table is only part of the primary key of this table
1206 if (null != createFolderTable)
1207 {
1208 foreach (Row row in createFolderTable.Rows)
1209 {
1210 Wix.CreateFolder createFolder = (Wix.CreateFolder)this.Core.GetIndexedElement(row);
1211 string directoryId = (string)row[0];
1212
1213 if (!createFolders.Contains(directoryId))
1214 {
1215 createFolders.Add(directoryId, new ArrayList());
1216 }
1217 ((ArrayList)createFolders[directoryId]).Add(createFolder);
1218 }
1219 }
1220
1221 if (null != secureObjectsTable)
1222 {
1223 foreach (Row row in secureObjectsTable.Rows)
1224 {
1225 string id = (string)row[0];
1226 string table = (string)row[1];
1227
1228 Util.PermissionEx permissionEx = (Util.PermissionEx)this.Core.GetIndexedElement(row);
1229
1230 if ("CreateFolder" == table)
1231 {
1232 ArrayList createFolderElements = (ArrayList)createFolders[id];
1233
1234 if (null != createFolderElements)
1235 {
1236 foreach (Wix.CreateFolder createFolder in createFolderElements)
1237 {
1238 createFolder.AddChild(permissionEx);
1239 }
1240 }
1241 else
1242 {
1243 this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, "SecureObjects", row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "LockObject", id, table));
1244 }
1245 }
1246 else
1247 {
1248 Wix.IParentElement parentElement = (Wix.IParentElement)this.Core.GetIndexedElement(table, id);
1249
1250 if (null != parentElement)
1251 {
1252 parentElement.AddChild(permissionEx);
1253 }
1254 else
1255 {
1256 this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, "SecureObjects", row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "LockObject", id, table));
1257 }
1258 }
1259 }
1260 }
1261 }
1262
1263 /// <summary>
1264 /// Finalize the ServiceConfig table.
1265 /// </summary>
1266 /// <param name="tables">The collection of all tables.</param>
1267 /// <remarks>
1268 /// Since there is no foreign key from the ServiceName column to the
1269 /// ServiceInstall table, this relationship must be handled late.
1270 /// </remarks>
1271 private void FinalizeServiceConfigTable(TableIndexedCollection tables)
1272 {
1273 Table serviceConfigTable = tables["ServiceConfig"];
1274 Table serviceInstallTable = tables["ServiceInstall"];
1275
1276 Hashtable serviceInstalls = new Hashtable();
1277
1278 // index the ServiceInstall table because the foreign key used by the ServiceConfig
1279 // table is actually the ServiceInstall.Name, not the ServiceInstall.ServiceInstall
1280 // this is unfortunate because the service Name is not guaranteed to be unique, so
1281 // decompiler must assume there could be multiple matches and add the ServiceConfig to each
1282 // TODO: the Component column information should be taken into acount to accurately identify
1283 // the correct column to use
1284 if (null != serviceInstallTable)
1285 {
1286 foreach (Row row in serviceInstallTable.Rows)
1287 {
1288 string name = (string)row[1];
1289 Wix.ServiceInstall serviceInstall = (Wix.ServiceInstall)this.Core.GetIndexedElement(row);
1290
1291 if (!serviceInstalls.Contains(name))
1292 {
1293 serviceInstalls.Add(name, new ArrayList());
1294 }
1295
1296 ((ArrayList)serviceInstalls[name]).Add(serviceInstall);
1297 }
1298 }
1299
1300 if (null != serviceConfigTable)
1301 {
1302 foreach (Row row in serviceConfigTable.Rows)
1303 {
1304 Util.ServiceConfig serviceConfig = (Util.ServiceConfig)this.Core.GetIndexedElement(row);
1305
1306 if (0 == (int)row[2])
1307 {
1308 Wix.Component component = (Wix.Component)this.Core.GetIndexedElement("Component", (string)row[1]);
1309
1310 if (null != component)
1311 {
1312 component.AddChild(serviceConfig);
1313 }
1314 else
1315 {
1316 this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, serviceConfigTable.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Component_", (string)row[1], "Component"));
1317 }
1318 }
1319 else
1320 {
1321 ArrayList serviceInstallElements = (ArrayList)serviceInstalls[row[0]];
1322
1323 if (null != serviceInstallElements)
1324 {
1325 foreach (Wix.ServiceInstall serviceInstall in serviceInstallElements)
1326 {
1327 serviceInstall.AddChild(serviceConfig);
1328 }
1329 }
1330 else
1331 {
1332 this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, serviceConfigTable.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "ServiceName", (string)row[0], "ServiceInstall"));
1333 }
1334 }
1335 }
1336 }
1337 }
1338
1339 /// <summary>
1340 /// Finalize the XmlConfig table.
1341 /// </summary>
1342 /// <param name="tables">Collection of all tables.</param>
1343 private void FinalizeXmlConfigTable(TableIndexedCollection tables)
1344 {
1345 Table xmlConfigTable = tables["XmlConfig"];
1346
1347 if (null != xmlConfigTable)
1348 {
1349 foreach (Row row in xmlConfigTable.Rows)
1350 {
1351 Util.XmlConfig xmlConfig = (Util.XmlConfig)this.Core.GetIndexedElement(row);
1352
1353 if (null == row[6] || 0 == (int)row[6])
1354 {
1355 Util.XmlConfig parentXmlConfig = (Util.XmlConfig)this.Core.GetIndexedElement("XmlConfig", (string)row[2]);
1356
1357 if (null != parentXmlConfig)
1358 {
1359 parentXmlConfig.AddChild(xmlConfig);
1360 }
1361 else
1362 {
1363 this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, xmlConfigTable.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "ElementPath", (string)row[2], "XmlConfig"));
1364 }
1365 }
1366 else
1367 {
1368 Wix.Component component = (Wix.Component)this.Core.GetIndexedElement("Component", (string)row[7]);
1369
1370 if (null != component)
1371 {
1372 component.AddChild(xmlConfig);
1373 }
1374 else
1375 {
1376 this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, xmlConfigTable.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Component_", (string)row[7], "Component"));
1377 }
1378 }
1379 }
1380 }
1381 }
1382
1383
1384 /// <summary>
1385 /// Finalize the XmlFile table.
1386 /// </summary>
1387 /// <param name="tables">The collection of all tables.</param>
1388 /// <remarks>
1389 /// Some of the XmlFile table rows are compiler generated from util:EventManifest node
1390 /// These rows should not be appended to component.
1391 /// </remarks>
1392 private void FinalizeXmlFileTable(TableIndexedCollection tables)
1393 {
1394 Table xmlFileTable = tables["XmlFile"];
1395 Table eventManifestTable = tables["EventManifest"];
1396
1397 if (null != xmlFileTable)
1398 {
1399 foreach (Row row in xmlFileTable.Rows)
1400 {
1401 bool bManifestGenerated = false;
1402 string xmlFileConfigId = (string)row[0];
1403 if (null != eventManifestTable)
1404 {
1405 foreach (Row emrow in eventManifestTable.Rows)
1406 {
1407 string formattedFile = (string)emrow[1];
1408 if ((formattedFile.StartsWith("[#", StringComparison.Ordinal) || formattedFile.StartsWith("[!", StringComparison.Ordinal))
1409 && formattedFile.EndsWith("]", StringComparison.Ordinal))
1410 {
1411 string fileId = formattedFile.Substring(2, formattedFile.Length - 3);
1412 if (String.Equals(String.Concat("Config_", fileId, "ResourceFile"), xmlFileConfigId))
1413 {
1414 Util.EventManifest eventManifest = (Util.EventManifest)this.Core.GetIndexedElement(emrow);
1415 if (null != eventManifest)
1416 {
1417 eventManifest.ResourceFile = (string)row[4];
1418 }
1419 bManifestGenerated = true;
1420 }
1421
1422 else if (String.Equals(String.Concat("Config_", fileId, "MessageFile"), xmlFileConfigId))
1423 {
1424 Util.EventManifest eventManifest = (Util.EventManifest)this.Core.GetIndexedElement(emrow);
1425 if (null != eventManifest)
1426 {
1427 eventManifest.MessageFile = (string)row[4];
1428 }
1429 bManifestGenerated = true;
1430 }
1431 }
1432 }
1433 }
1434
1435 if (true == bManifestGenerated)
1436 continue;
1437
1438 Util.XmlFile xmlFile = new Util.XmlFile();
1439
1440 xmlFile.Id = (string)row[0];
1441 xmlFile.File = (string)row[1];
1442 xmlFile.ElementPath = (string)row[2];
1443
1444 if (null != row[3])
1445 {
1446 xmlFile.Name = (string)row[3];
1447 }
1448
1449 if (null != row[4])
1450 {
1451 xmlFile.Value = (string)row[4];
1452 }
1453
1454 int flags = (int)row[5];
1455 if (0x1 == (flags & 0x1) && 0x2 == (flags & 0x2))
1456 {
1457 this.Core.OnMessage(WixWarnings.IllegalColumnValue(row.SourceLineNumbers, xmlFileTable.Name, row.Fields[5].Column.Name, row[5]));
1458 }
1459 else if (0x1 == (flags & 0x1))
1460 {
1461 xmlFile.Action = Util.XmlFile.ActionType.createElement;
1462 }
1463 else if (0x2 == (flags & 0x2))
1464 {
1465 xmlFile.Action = Util.XmlFile.ActionType.deleteValue;
1466 }
1467 else
1468 {
1469 xmlFile.Action = Util.XmlFile.ActionType.setValue;
1470 }
1471
1472 if (0x100 == (flags & 0x100))
1473 {
1474 xmlFile.SelectionLanguage = Util.XmlFile.SelectionLanguageType.XPath;
1475 }
1476
1477 if (0x00001000 == (flags & 0x00001000))
1478 {
1479 xmlFile.PreserveModifiedDate = Util.YesNoType.yes;
1480 }
1481
1482 if (0x00010000 == (flags & 0x00010000))
1483 {
1484 xmlFile.Permanent = Util.YesNoType.yes;
1485 }
1486
1487 if (null != row[7])
1488 {
1489 xmlFile.Sequence = (int)row[7];
1490 }
1491
1492 Wix.Component component = (Wix.Component)this.Core.GetIndexedElement("Component", (string)row[6]);
1493
1494 if (null != component)
1495 {
1496 component.AddChild(xmlFile);
1497 }
1498 else
1499 {
1500 this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, xmlFileTable.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Component_", (string)row[6], "Component"));
1501 }
1502 }
1503 }
1504 }
1505
1506 /// <summary>
1507 /// Finalize the eventManifest table.
1508 /// This function must be called after FinalizeXmlFileTable
1509 /// </summary>
1510 /// <param name="tables">The collection of all tables.</param>
1511 private void FinalizeEventManifestTable(TableIndexedCollection tables)
1512 {
1513 Table eventManifestTable = tables["EventManifest"];
1514
1515 if (null != eventManifestTable)
1516 {
1517 foreach (Row row in eventManifestTable.Rows)
1518 {
1519 string formattedFile = (string)row[1];
1520 Util.EventManifest eventManifest = (Util.EventManifest)this.Core.GetIndexedElement(row);
1521
1522 // try to "de-format" the File column's value to determine the proper parent File element
1523 if ((formattedFile.StartsWith("[#", StringComparison.Ordinal) || formattedFile.StartsWith("[!", StringComparison.Ordinal))
1524 && formattedFile.EndsWith("]", StringComparison.Ordinal))
1525 {
1526 string fileId = formattedFile.Substring(2, formattedFile.Length - 3);
1527
1528 Wix.File file = (Wix.File)this.Core.GetIndexedElement("File", fileId);
1529 if (null != file)
1530 {
1531 file.AddChild(eventManifest);
1532 }
1533 }
1534 else
1535 {
1536 this.Core.OnMessage(UtilErrors.IllegalFileValueInPerfmonOrManifest(formattedFile, "EventManifest"));
1537 }
1538 }
1539 }
1540 }
1541 }
1542#endif
1543}
diff --git a/src/ext/Util/wixext/UtilErrors.cs b/src/ext/Util/wixext/UtilErrors.cs
new file mode 100644
index 00000000..b9ce1688
--- /dev/null
+++ b/src/ext/Util/wixext/UtilErrors.cs
@@ -0,0 +1,49 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Util
4{
5 using System;
6 using System.Resources;
7 using WixToolset.Data;
8
9 public static class UtilErrors
10 {
11 public static Message IllegalAttributeWithoutComponent(SourceLineNumber sourceLineNumbers, string elementName, string attributeName)
12 {
13 return Message(sourceLineNumbers, Ids.IllegalAttributeWithoutComponent, "The {0}/@{1} attribute cannot be specified unless the element has a Component as an ancestor. A {0} that does not have a Component ancestor is not installed.", elementName, attributeName);
14 }
15
16 public static Message IllegalElementWithoutComponent(SourceLineNumber sourceLineNumbers, string elementName)
17 {
18 return Message(sourceLineNumbers, Ids.IllegalElementWithoutComponent, "The {0} element cannot be specified unless the element has a Component as an ancestor. A {0} that does not have a Component ancestor is not installed.", elementName);
19 }
20
21 public static Message IllegalFileValueInPerfmonOrManifest(string file, string table)
22 {
23 return Message(null, Ids.IllegalFileValueInPerfmonOrManifest, "The value '{0}' in the File column, {1} table is invalid. It should be in the form of '[#file]' or '[!file]'.", file, table);
24 }
25
26 public static Message InvalidRegistryObject(SourceLineNumber sourceLineNumbers, string registryElementName)
27 {
28 return Message(sourceLineNumbers, Ids.InvalidRegistryObject, "The {0} element has no id and cannot have its permissions set. If you want to set permissions on a 'placeholder' registry key, force its creation by setting the ForceCreateOnInstall attribute to yes.", registryElementName);
29 }
30
31 private static Message Message(SourceLineNumber sourceLineNumber, Ids id, string format, params object[] args)
32 {
33 return new Message(sourceLineNumber, MessageLevel.Error, (int)id, format, args);
34 }
35
36 private static Message Message(SourceLineNumber sourceLineNumber, Ids id, ResourceManager resourceManager, string resourceName, params object[] args)
37 {
38 return new Message(sourceLineNumber, MessageLevel.Error, (int)id, resourceManager, resourceName, args);
39 }
40
41 public enum Ids
42 {
43 IllegalAttributeWithoutComponent = 5050,
44 IllegalElementWithoutComponent = 5051,
45 IllegalFileValueInPerfmonOrManifest = 5054,
46 InvalidRegistryObject = 5063,
47 }
48 }
49}
diff --git a/src/ext/Util/wixext/UtilExtensionData.cs b/src/ext/Util/wixext/UtilExtensionData.cs
new file mode 100644
index 00000000..d3ca3358
--- /dev/null
+++ b/src/ext/Util/wixext/UtilExtensionData.cs
@@ -0,0 +1,23 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Util
4{
5 using WixToolset.Data;
6 using WixToolset.Extensibility;
7
8 public sealed class UtilExtensionData : BaseExtensionData
9 {
10 public override string DefaultCulture => "en-US";
11
12 public override bool TryGetSymbolDefinitionByName(string name, out IntermediateSymbolDefinition symbolDefinition)
13 {
14 symbolDefinition = UtilSymbolDefinitions.ByName(name);
15 return symbolDefinition != null;
16 }
17
18 public override Intermediate GetLibrary(ISymbolDefinitionCreator symbolDefinitions)
19 {
20 return Intermediate.Load(typeof(UtilExtensionData).Assembly, "WixToolset.Util.util.wixlib", symbolDefinitions);
21 }
22 }
23}
diff --git a/src/ext/Util/wixext/UtilExtensionFactory.cs b/src/ext/Util/wixext/UtilExtensionFactory.cs
new file mode 100644
index 00000000..08352813
--- /dev/null
+++ b/src/ext/Util/wixext/UtilExtensionFactory.cs
@@ -0,0 +1,18 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Util
4{
5 using System;
6 using System.Collections.Generic;
7 using WixToolset.Extensibility;
8
9 public class UtilExtensionFactory : BaseExtensionFactory
10 {
11 protected override IReadOnlyCollection<Type> ExtensionTypes => new[]
12 {
13 typeof(UtilCompiler),
14 typeof(UtilExtensionData),
15 typeof(UtilWindowsInstallerBackendBinderExtension),
16 };
17 }
18}
diff --git a/src/ext/Util/wixext/UtilTableDefinitions.cs b/src/ext/Util/wixext/UtilTableDefinitions.cs
new file mode 100644
index 00000000..12f423cc
--- /dev/null
+++ b/src/ext/Util/wixext/UtilTableDefinitions.cs
@@ -0,0 +1,319 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Util
4{
5 using WixToolset.Data.WindowsInstaller;
6
7 public static class UtilTableDefinitions
8 {
9 public static readonly TableDefinition Wix4CloseApplication = new TableDefinition(
10 "Wix4CloseApplication",
11 UtilSymbolDefinitions.WixCloseApplication,
12 new[]
13 {
14 new ColumnDefinition("Wix4CloseApplication", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, description: "Primary key, non-localized token in table.", modularizeType: ColumnModularizeType.Column),
15 new ColumnDefinition("Target", ColumnType.Localized, 0, primaryKey: false, nullable: false, ColumnCategory.Formatted, description: "Name of executable to ensure is closed.", modularizeType: ColumnModularizeType.Property),
16 new ColumnDefinition("Description", ColumnType.String, 0, primaryKey: false, nullable: true, ColumnCategory.Formatted, description: "Description string displayed to user when executable is in use.", modularizeType: ColumnModularizeType.Property, forceLocalizable: true),
17 new ColumnDefinition("Condition", ColumnType.String, 0, primaryKey: false, nullable: true, ColumnCategory.Condition, description: "Optional expression which skips the closing.", modularizeType: ColumnModularizeType.Condition, forceLocalizable: true),
18 new ColumnDefinition("Attributes", ColumnType.Number, 4, primaryKey: false, nullable: false, ColumnCategory.Unknown, minValue: 0, maxValue: 2147483647, description: "A 32-bit word that specifies the attribute flags to be applied."),
19 new ColumnDefinition("Sequence", ColumnType.Number, 4, primaryKey: false, nullable: true, ColumnCategory.Unknown, minValue: 1, maxValue: 2147483647, description: "Sequence to order the closings by."),
20 new ColumnDefinition("Property", ColumnType.String, 72, primaryKey: false, nullable: true, ColumnCategory.Identifier, description: "Optional property that is set to the number of running instances of the app.", modularizeType: ColumnModularizeType.Property, forceLocalizable: true),
21 new ColumnDefinition("TerminateExitCode", ColumnType.Number, 4, primaryKey: false, nullable: true, ColumnCategory.Unknown, minValue: 0, maxValue: 2147483647, description: "Exit code to return from a terminated application."),
22 new ColumnDefinition("Timeout", ColumnType.Number, 4, primaryKey: false, nullable: true, ColumnCategory.Unknown, minValue: 1, maxValue: 2147483647, description: "Timeout in milliseconds before scheduling restart or terminating application."),
23 },
24 symbolIdIsPrimaryKey: true
25 );
26
27 public static readonly TableDefinition Wix4RemoveFolderEx = new TableDefinition(
28 "Wix4RemoveFolderEx",
29 UtilSymbolDefinitions.WixRemoveFolderEx,
30 new[]
31 {
32 new ColumnDefinition("Wix4RemoveFolderEx", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, description: "Identifier for the WixRemoveFolderEx row in the package.", modularizeType: ColumnModularizeType.Column),
33 new ColumnDefinition("Component_", ColumnType.String, 72, primaryKey: false, nullable: false, ColumnCategory.Identifier, keyTable: "Component", keyColumn: 1, description: "Foreign key into the Component table used to determine install state", modularizeType: ColumnModularizeType.Column),
34 new ColumnDefinition("Property", ColumnType.String, 72, primaryKey: false, nullable: false, ColumnCategory.Identifier, description: "Name of Property that contains the root of the directory tree to remove.", modularizeType: ColumnModularizeType.Column),
35 new ColumnDefinition("InstallMode", ColumnType.Number, 2, primaryKey: false, nullable: false, ColumnCategory.Unknown, minValue: 1, maxValue: 3, description: "1 == Remove only when the associated component is being installed (msiInstallStateLocal or msiInstallStateSource), 2 == Remove only when the associated component is being removed (msiInstallStateAbsent), 3 = Remove in either of the above cases."),
36 new ColumnDefinition("Condition", ColumnType.String, 0, primaryKey: false, nullable: true, ColumnCategory.Condition, description: "Optional expression which skips the removing of folders.", modularizeType: ColumnModularizeType.Condition, forceLocalizable: true),
37 },
38 symbolIdIsPrimaryKey: true
39 );
40
41 public static readonly TableDefinition Wix4RemoveRegistryKeyEx = new TableDefinition(
42 "Wix4RemoveRegistryKeyEx",
43 UtilSymbolDefinitions.WixRemoveRegistryKeyEx,
44 new[]
45 {
46 new ColumnDefinition("Wix4RemoveRegistryKeyEx", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, description: "Identifier for the Wix4RemoveRegistryKeyEx row in the package.", modularizeType: ColumnModularizeType.Column),
47 new ColumnDefinition("Component_", ColumnType.String, 72, primaryKey: false, nullable: false, ColumnCategory.Identifier, keyTable: "Component", keyColumn: 1, description: "Foreign key into the Component table used to determine install state", modularizeType: ColumnModularizeType.Column),
48 new ColumnDefinition("Root", ColumnType.Number, 2, primaryKey: false, nullable: false, ColumnCategory.Unknown, minValue: -1, maxValue: 3, description: "The predefined root key for the registry value, one of rrkEnum."),
49 new ColumnDefinition("Key", ColumnType.Localized, 255, primaryKey: false, nullable: false, ColumnCategory.RegPath, description: "The key for the registry value.", modularizeType: ColumnModularizeType.Property),
50 new ColumnDefinition("InstallMode", ColumnType.Number, 2, primaryKey: false, nullable: false, ColumnCategory.Unknown, minValue: 1, maxValue: 3, description: "1 == Remove only when the associated component is being installed (msiInstallStateLocal or msiInstallStateSource), 2 == Remove only when the associated component is being removed (msiInstallStateAbsent), 3 = Remove in either of the above cases."),
51 new ColumnDefinition("Condition", ColumnType.String, 0, primaryKey: false, nullable: true, ColumnCategory.Condition, description: "Optional expression to control whether the registry key is removed.", modularizeType: ColumnModularizeType.Condition, forceLocalizable: true),
52 },
53 symbolIdIsPrimaryKey: true
54 );
55
56 public static readonly TableDefinition Wix4RestartResource = new TableDefinition(
57 "Wix4RestartResource",
58 UtilSymbolDefinitions.WixRestartResource,
59 new[]
60 {
61 new ColumnDefinition("Wix4RestartResource", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, description: "Primary key, non-localized identifier.", modularizeType: ColumnModularizeType.Column),
62 new ColumnDefinition("Component_", ColumnType.String, 72, primaryKey: false, nullable: true, ColumnCategory.Identifier, keyTable: "Component", keyColumn: 1, description: "Foreign key into the Component table used to determine install state.", modularizeType: ColumnModularizeType.Column),
63 new ColumnDefinition("Resource", ColumnType.String, 0, primaryKey: false, nullable: false, ColumnCategory.Formatted, description: "The resource to be registered with the Restart Manager.", modularizeType: ColumnModularizeType.Property),
64 new ColumnDefinition("Attributes", ColumnType.Number, 4, primaryKey: false, nullable: false, ColumnCategory.Unknown, minValue: 0, maxValue: 2147483647, description: "A 32-bit word that specifies the type of resource and flags used for processing."),
65 },
66 symbolIdIsPrimaryKey: true
67 );
68
69 public static readonly TableDefinition Wix4FileShare = new TableDefinition(
70 "Wix4FileShare",
71 UtilSymbolDefinitions.FileShare,
72 new[]
73 {
74 new ColumnDefinition("Wix4FileShare", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, description: "Primary key, non-localized identifier", modularizeType: ColumnModularizeType.Column),
75 new ColumnDefinition("ShareName", ColumnType.String, 255, primaryKey: false, nullable: false, ColumnCategory.Formatted, description: "The actual share name used"),
76 new ColumnDefinition("Component_", ColumnType.String, 72, primaryKey: false, nullable: false, ColumnCategory.Identifier, keyTable: "Component", keyColumn: 1, description: "Foreign key into the Component table used to determine install state", modularizeType: ColumnModularizeType.Column),
77 new ColumnDefinition("Description", ColumnType.String, 255, primaryKey: false, nullable: true, ColumnCategory.Text, description: "Description string displayed for the file share"),
78 new ColumnDefinition("Directory_", ColumnType.String, 72, primaryKey: false, nullable: false, ColumnCategory.Identifier, keyTable: "Directory", keyColumn: 1, description: "Foreign key referencing directory that the share is created on", modularizeType: ColumnModularizeType.Column),
79 },
80 symbolIdIsPrimaryKey: true
81 );
82
83 public static readonly TableDefinition Wix4FileSharePermissions = new TableDefinition(
84 "Wix4FileSharePermissions",
85 UtilSymbolDefinitions.FileSharePermissions,
86 new[]
87 {
88 new ColumnDefinition("Wix4FileShare_", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, keyTable: "FileShare", keyColumn: 1, description: "FileShare that these premissions are to be applied to.", modularizeType: ColumnModularizeType.Column),
89 new ColumnDefinition("Wix4User_", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, keyTable: "Wix4User", description: "User that these premissions are to apply to.", modularizeType: ColumnModularizeType.Column),
90 new ColumnDefinition("Permissions", ColumnType.Number, 4, primaryKey: false, nullable: false, ColumnCategory.Unknown, description: "Permissions int, as in EXPLICIT_ACCESS.grfAccessPermissions in MSDN"),
91 },
92 symbolIdIsPrimaryKey: false
93 );
94
95 public static readonly TableDefinition Wix4Group = new TableDefinition(
96 "Wix4Group",
97 UtilSymbolDefinitions.Group,
98 new[]
99 {
100 new ColumnDefinition("Wix4Group", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, description: "Primary key, non-localized token", modularizeType: ColumnModularizeType.Column),
101 new ColumnDefinition("Component_", ColumnType.String, 72, primaryKey: false, nullable: true, ColumnCategory.Text, keyTable: "Component", keyColumn: 1, description: "Foreign key, Component used to determine install state", modularizeType: ColumnModularizeType.Column),
102 new ColumnDefinition("Name", ColumnType.String, 255, primaryKey: false, nullable: false, ColumnCategory.Formatted, description: "Group name", modularizeType: ColumnModularizeType.Property),
103 new ColumnDefinition("Domain", ColumnType.String, 255, primaryKey: false, nullable: true, ColumnCategory.Formatted, description: "Group domain", modularizeType: ColumnModularizeType.Property),
104 },
105 symbolIdIsPrimaryKey: true
106 );
107
108 public static readonly TableDefinition Wix4InternetShortcut = new TableDefinition(
109 "Wix4InternetShortcut",
110 UtilSymbolDefinitions.WixInternetShortcut,
111 new[]
112 {
113 new ColumnDefinition("Wix4InternetShortcut", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, description: "Primary key, non-localized token in table.", modularizeType: ColumnModularizeType.Column),
114 new ColumnDefinition("Component_", ColumnType.String, 72, primaryKey: false, nullable: false, ColumnCategory.Text, keyTable: "Component", keyColumn: 1, description: "Foreign key, Component used to determine install state", modularizeType: ColumnModularizeType.Column),
115 new ColumnDefinition("Directory_", ColumnType.String, 72, primaryKey: false, nullable: false, ColumnCategory.Identifier, keyTable: "Directory", keyColumn: 1, description: "Foreign key referencing directory that the shortcut is created in", modularizeType: ColumnModularizeType.Column),
116 new ColumnDefinition("Name", ColumnType.String, 72, primaryKey: false, nullable: false, ColumnCategory.Text, description: "Name used for shortcut.", modularizeType: ColumnModularizeType.Property),
117 new ColumnDefinition("Target", ColumnType.Localized, 0, primaryKey: false, nullable: false, ColumnCategory.Text, description: "URL target."),
118 new ColumnDefinition("Attributes", ColumnType.Number, 2, primaryKey: false, nullable: false, ColumnCategory.Unknown, description: "Attribute flags that control how the shortcut is created."),
119 new ColumnDefinition("IconFile", ColumnType.Localized, 0, primaryKey: false, nullable: true, ColumnCategory.Formatted, description: "Icon file for shortcut", modularizeType: ColumnModularizeType.Property),
120 new ColumnDefinition("IconIndex", ColumnType.Number, 4, primaryKey: false, nullable: true, ColumnCategory.Unknown, description: "Index of the icon being referenced."),
121 },
122 symbolIdIsPrimaryKey: true
123 );
124
125 public static readonly TableDefinition Wix4PerformanceCategory = new TableDefinition(
126 "Wix4PerformanceCategory",
127 UtilSymbolDefinitions.PerformanceCategory,
128 new[]
129 {
130 new ColumnDefinition("Wix4PerformanceCategory", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, description: "Primary key, non-localized token in table.", modularizeType: ColumnModularizeType.Column),
131 new ColumnDefinition("Component_", ColumnType.String, 72, primaryKey: false, nullable: false, ColumnCategory.Identifier, keyTable: "Component", keyColumn: 1, description: "Component used to determine install state", modularizeType: ColumnModularizeType.Column),
132 new ColumnDefinition("Name", ColumnType.String, 80, primaryKey: false, nullable: false, ColumnCategory.Text, description: "Name of the performance counter category."),
133 new ColumnDefinition("IniData", ColumnType.Localized, 0, primaryKey: false, nullable: false, ColumnCategory.Text, description: "Data that goes into the performance counter .ini file."),
134 new ColumnDefinition("ConstantData", ColumnType.Localized, 0, primaryKey: false, nullable: false, ColumnCategory.Text, description: "Data that goes into the performance counter .h file."),
135 },
136 symbolIdIsPrimaryKey: true
137 );
138
139 public static readonly TableDefinition Wix4Perfmon = new TableDefinition(
140 "Wix4Perfmon",
141 UtilSymbolDefinitions.Perfmon,
142 new[]
143 {
144 new ColumnDefinition("Component_", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, keyTable: "Component", keyColumn: 1, description: "Component used to determine install state", modularizeType: ColumnModularizeType.Column),
145 new ColumnDefinition("File", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Formatted, description: "Name of .INI file", modularizeType: ColumnModularizeType.Property),
146 new ColumnDefinition("Name", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Text, description: "Service name in registry"),
147 },
148 symbolIdIsPrimaryKey: false
149 );
150
151 public static readonly TableDefinition Wix4PerfmonManifest = new TableDefinition(
152 "Wix4PerfmonManifest",
153 UtilSymbolDefinitions.PerfmonManifest,
154 new[]
155 {
156 new ColumnDefinition("Component_", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, keyTable: "Component", keyColumn: 1, description: "Component used to determine install state", modularizeType: ColumnModularizeType.Column),
157 new ColumnDefinition("File", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Formatted, description: "Name of perfmon manifest file", modularizeType: ColumnModularizeType.Property),
158 new ColumnDefinition("ResourceFileDirectory", ColumnType.String, 255, primaryKey: true, nullable: false, ColumnCategory.Formatted, description: "The path of the Resource File Directory"),
159 },
160 symbolIdIsPrimaryKey: false
161 );
162
163 public static readonly TableDefinition Wix4EventManifest = new TableDefinition(
164 "Wix4EventManifest",
165 UtilSymbolDefinitions.EventManifest,
166 new[]
167 {
168 new ColumnDefinition("Component_", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, keyTable: "Component", keyColumn: 1, description: "Component used to determine install state", modularizeType: ColumnModularizeType.Column),
169 new ColumnDefinition("File", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Formatted, description: "Name of event manifest file", modularizeType: ColumnModularizeType.Property),
170 },
171 symbolIdIsPrimaryKey: false
172 );
173
174 public static readonly TableDefinition Wix4SecureObject = new TableDefinition(
175 "Wix4SecureObject",
176 UtilSymbolDefinitions.SecureObjects,
177 new[]
178 {
179 new ColumnDefinition("Wix4SecureObject", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, description: "Primary key, non-localized token in Table", modularizeType: ColumnModularizeType.Column),
180 new ColumnDefinition("Table", ColumnType.String, 32, primaryKey: true, nullable: false, ColumnCategory.Text, description: "Table SecureObject should be securing"),
181 new ColumnDefinition("Domain", ColumnType.String, 255, primaryKey: true, nullable: true, ColumnCategory.Text, description: "Domain half of user account to secure", modularizeType: ColumnModularizeType.Property),
182 new ColumnDefinition("User", ColumnType.String, 255, primaryKey: true, nullable: false, ColumnCategory.Text, description: "User name half of user account to secure", modularizeType: ColumnModularizeType.Property),
183 new ColumnDefinition("Attributes", ColumnType.Number, 4, primaryKey: false, nullable: false, ColumnCategory.Integer, minValue: 0, maxValue: 2147483647, description: "A 32-bit word that specifies the attribute flags to be applied."),
184 new ColumnDefinition("Permission", ColumnType.Number, 4, primaryKey: false, nullable: true, ColumnCategory.Unknown, minValue: -2147483647, maxValue: 2147483647, description: "Permissions to grant to User"),
185 new ColumnDefinition("Component_", ColumnType.String, 72, primaryKey: false, nullable: false, ColumnCategory.Identifier, keyTable: "Component", keyColumn: 1, description: "Foreign key into the Component table used to determine install state", modularizeType: ColumnModularizeType.Column),
186 },
187 symbolIdIsPrimaryKey: false
188 );
189
190 public static readonly TableDefinition Wix4ServiceConfig = new TableDefinition(
191 "Wix4ServiceConfig",
192 UtilSymbolDefinitions.ServiceConfig,
193 new[]
194 {
195 new ColumnDefinition("ServiceName", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Formatted, description: "Primary key, non-localized token"),
196 new ColumnDefinition("Component_", ColumnType.String, 72, primaryKey: false, nullable: false, ColumnCategory.Identifier, keyTable: "Component", keyColumn: 1, description: "Foreign key, Component used to determine install state ", modularizeType: ColumnModularizeType.Column),
197 new ColumnDefinition("NewService", ColumnType.Number, 1, primaryKey: false, nullable: false, ColumnCategory.Unknown, minValue: 0, maxValue: 1, description: "Whether the affected service is being installed or already exists."),
198 new ColumnDefinition("FirstFailureActionType", ColumnType.String, 32, primaryKey: false, nullable: false, ColumnCategory.Text, description: "First failure action type for configured service to take."),
199 new ColumnDefinition("SecondFailureActionType", ColumnType.String, 32, primaryKey: false, nullable: false, ColumnCategory.Text, description: "Second failure action type for configured service to take."),
200 new ColumnDefinition("ThirdFailureActionType", ColumnType.String, 32, primaryKey: false, nullable: false, ColumnCategory.Text, description: "Third failure action type for configured service to take."),
201 new ColumnDefinition("ResetPeriodInDays", ColumnType.Number, 4, primaryKey: false, nullable: true, ColumnCategory.Integer, minValue: 0, description: "Period after which to reset the failure count for the service."),
202 new ColumnDefinition("RestartServiceDelayInSeconds", ColumnType.Number, 4, primaryKey: false, nullable: true, ColumnCategory.Integer, minValue: 0, description: "Period after which to restart the service after a given failure."),
203 new ColumnDefinition("ProgramCommandLine", ColumnType.String, 255, primaryKey: false, nullable: true, ColumnCategory.Formatted, description: "Command line for program to run if failure action is RUN_COMMAND."),
204 new ColumnDefinition("RebootMessage", ColumnType.String, 255, primaryKey: false, nullable: true, ColumnCategory.Text, description: "Message to show to users when rebooting if failure action is REBOOT."),
205 },
206 symbolIdIsPrimaryKey: false
207 );
208
209 public static readonly TableDefinition Wix4TouchFile = new TableDefinition(
210 "Wix4TouchFile",
211 UtilSymbolDefinitions.WixTouchFile,
212 new[]
213 {
214 new ColumnDefinition("Wix4TouchFile", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, description: "Identifier for the Wix4TouchFile row in the package.", modularizeType: ColumnModularizeType.Column),
215 new ColumnDefinition("Component_", ColumnType.String, 72, primaryKey: false, nullable: false, ColumnCategory.Identifier, keyTable: "Component", keyColumn: 1, description: "Foreign key into the Component table used to determine install state", modularizeType: ColumnModularizeType.Column),
216 new ColumnDefinition("Path", ColumnType.String, 255, primaryKey: false, nullable: false, ColumnCategory.Formatted, description: "Formatted column that resolves to the path to touch.", modularizeType: ColumnModularizeType.Property),
217 new ColumnDefinition("Attributes", ColumnType.Number, 2, primaryKey: false, nullable: false, ColumnCategory.Unknown, minValue: 1, maxValue: 63, description: "1 == Touch only when the associated component is being installed, 2 == Touch only when the associated component is being repaired , 4 == Touch only when the associated component is being removed, 16 = path is in 64-bit location, 32 = touching the file is vital."),
218 },
219 symbolIdIsPrimaryKey: true
220 );
221
222 public static readonly TableDefinition Wix4User = new TableDefinition(
223 "Wix4User",
224 UtilSymbolDefinitions.User,
225 new[]
226 {
227 new ColumnDefinition("Wix4User", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, description: "Primary key, non-localized token", modularizeType: ColumnModularizeType.Column),
228 new ColumnDefinition("Component_", ColumnType.String, 72, primaryKey: false, nullable: true, ColumnCategory.Text, keyTable: "Component", keyColumn: 1, description: "Foreign key, Component used to determine install state", modularizeType: ColumnModularizeType.Column),
229 new ColumnDefinition("Name", ColumnType.String, 255, primaryKey: false, nullable: false, ColumnCategory.Formatted, description: "User name", modularizeType: ColumnModularizeType.Property),
230 new ColumnDefinition("Domain", ColumnType.String, 255, primaryKey: false, nullable: true, ColumnCategory.Formatted, description: "User domain", modularizeType: ColumnModularizeType.Property),
231 new ColumnDefinition("Password", ColumnType.String, 255, primaryKey: false, nullable: true, ColumnCategory.Formatted, description: "User password", modularizeType: ColumnModularizeType.Property),
232 new ColumnDefinition("Attributes", ColumnType.Number, 4, primaryKey: false, nullable: true, ColumnCategory.Unknown, minValue: 0, maxValue: 65535, description: "Attributes describing how to create the user"),
233 },
234 symbolIdIsPrimaryKey: true
235 );
236
237 public static readonly TableDefinition Wix4UserGroup = new TableDefinition(
238 "Wix4UserGroup",
239 UtilSymbolDefinitions.UserGroup,
240 new[]
241 {
242 new ColumnDefinition("Wix4User_", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, keyTable: "Wix4User", keyColumn: 1, description: "User to be joined to a Group.", modularizeType: ColumnModularizeType.Column),
243 new ColumnDefinition("Wix4Group_", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, keyTable: "Wix4Group", keyColumn: 1, description: "Group to join User to.", modularizeType: ColumnModularizeType.Column),
244 },
245 symbolIdIsPrimaryKey: false
246 );
247
248 public static readonly TableDefinition Wix4XmlFile = new TableDefinition(
249 "Wix4XmlFile",
250 UtilSymbolDefinitions.XmlFile,
251 new[]
252 {
253 new ColumnDefinition("Wix4XmlFile", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, description: "Primary key, non-localized token.", modularizeType: ColumnModularizeType.Column),
254 new ColumnDefinition("File", ColumnType.Localized, 255, primaryKey: false, nullable: false, ColumnCategory.Formatted, description: "The .XML file in which to write the information", modularizeType: ColumnModularizeType.Property),
255 new ColumnDefinition("ElementPath", ColumnType.Localized, 0, primaryKey: false, nullable: false, ColumnCategory.Formatted, description: "The .XML file element to modify.", modularizeType: ColumnModularizeType.Property),
256 new ColumnDefinition("Name", ColumnType.Localized, 255, primaryKey: false, nullable: true, ColumnCategory.Formatted, description: "The .XML file node to set/add in the element.", modularizeType: ColumnModularizeType.Property),
257 new ColumnDefinition("Value", ColumnType.Localized, 0, primaryKey: false, nullable: true, ColumnCategory.Formatted, description: "The value to be written.", modularizeType: ColumnModularizeType.Property),
258 new ColumnDefinition("Flags", ColumnType.Number, 4, primaryKey: false, nullable: false, ColumnCategory.Unknown, minValue: 0, maxValue: 70143, description: "Flags"),
259 new ColumnDefinition("Component_", ColumnType.String, 72, primaryKey: false, nullable: false, ColumnCategory.Identifier, keyTable: "Component", keyColumn: 1, description: "Foreign key into the Component table referencing component that controls the installing of the .XML value.", modularizeType: ColumnModularizeType.Column),
260 new ColumnDefinition("Sequence", ColumnType.Number, 2, primaryKey: false, nullable: true, ColumnCategory.Unknown, description: "Order to execute the XML modifications."),
261 },
262 symbolIdIsPrimaryKey: true
263 );
264
265 public static readonly TableDefinition Wix4XmlConfig = new TableDefinition(
266 "Wix4XmlConfig",
267 UtilSymbolDefinitions.XmlConfig,
268 new[]
269 {
270 new ColumnDefinition("Wix4XmlConfig", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, description: "Primary key, non-localized token.", modularizeType: ColumnModularizeType.Column),
271 new ColumnDefinition("File", ColumnType.Localized, 255, primaryKey: false, nullable: false, ColumnCategory.Formatted, description: "The .XML file in which to write the information", modularizeType: ColumnModularizeType.Property),
272 new ColumnDefinition("ElementId", ColumnType.String, 0, primaryKey: false, nullable: true, ColumnCategory.Identifier, keyTable: "Wix4XmlConfig", keyColumn: 1, description: "A foreign key reference to another Wix4XmlConfig row if no attributes are set and the row referenced is a create element row.", modularizeType: ColumnModularizeType.Column),
273 new ColumnDefinition("ElementPath", ColumnType.Localized, 0, primaryKey: false, nullable: true, ColumnCategory.Formatted, description: "The XPATH query for an element to modify or add children to. Must be null if ElementId is provided", modularizeType: ColumnModularizeType.Property),
274 new ColumnDefinition("VerifyPath", ColumnType.Localized, 0, primaryKey: false, nullable: true, ColumnCategory.Formatted, description: "The XPATH query run from ElementPath to verify whether a repair is necessary. Also used to uninstall.", modularizeType: ColumnModularizeType.Property),
275 new ColumnDefinition("Name", ColumnType.Localized, 255, primaryKey: false, nullable: true, ColumnCategory.Formatted, description: "The .XML file node to set/add in the element.", modularizeType: ColumnModularizeType.Property),
276 new ColumnDefinition("Value", ColumnType.Localized, 0, primaryKey: false, nullable: true, ColumnCategory.Formatted, description: "The value to be written.", modularizeType: ColumnModularizeType.Property),
277 new ColumnDefinition("Flags", ColumnType.Number, 4, primaryKey: false, nullable: false, ColumnCategory.Unknown, minValue: 0, maxValue: 65536, description: "Element=1,Value=2,Document=4,Create=16,Delete=32,Install=256,Uninstall=512"),
278 new ColumnDefinition("Component_", ColumnType.String, 72, primaryKey: false, nullable: false, ColumnCategory.Identifier, keyTable: "Component", keyColumn: 1, description: "Foreign key into the Component table referencing component that controls the installing of the .XML value.", modularizeType: ColumnModularizeType.Column),
279 new ColumnDefinition("Sequence", ColumnType.Number, 2, primaryKey: false, nullable: true, ColumnCategory.Unknown, description: "Order to execute the XML modifications."),
280 },
281 symbolIdIsPrimaryKey: true
282 );
283
284 public static readonly TableDefinition Wix4FormatFile = new TableDefinition(
285 "Wix4FormatFile",
286 UtilSymbolDefinitions.WixFormatFiles,
287 new[]
288 {
289 new ColumnDefinition("Binary_", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, keyTable: "Binary", keyColumn: 1, description: "Binary data to be formatted.", modularizeType: ColumnModularizeType.Column),
290 new ColumnDefinition("File_", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, keyTable: "File", keyColumn: 1, description: "File whose component controls the custom action and where the formatted data is written.", modularizeType: ColumnModularizeType.Column),
291 },
292 symbolIdIsPrimaryKey: false
293 );
294
295 public static readonly TableDefinition[] All = new[]
296 {
297 Wix4CloseApplication,
298 Wix4RemoveFolderEx,
299 Wix4RemoveRegistryKeyEx,
300 Wix4RestartResource,
301 Wix4FileShare,
302 Wix4FileSharePermissions,
303 Wix4Group,
304 Wix4InternetShortcut,
305 Wix4PerformanceCategory,
306 Wix4Perfmon,
307 Wix4PerfmonManifest,
308 Wix4EventManifest,
309 Wix4SecureObject,
310 Wix4ServiceConfig,
311 Wix4TouchFile,
312 Wix4User,
313 Wix4UserGroup,
314 Wix4XmlFile,
315 Wix4XmlConfig,
316 Wix4FormatFile,
317 };
318 }
319}
diff --git a/src/ext/Util/wixext/UtilWarnings.cs b/src/ext/Util/wixext/UtilWarnings.cs
new file mode 100644
index 00000000..b65abe45
--- /dev/null
+++ b/src/ext/Util/wixext/UtilWarnings.cs
@@ -0,0 +1,37 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Util
4{
5 using System;
6 using System.Resources;
7 using WixToolset.Data;
8
9 public static class UtilWarnings
10 {
11 public static Message DeprecatedPerfCounterElement(SourceLineNumber sourceLineNumbers)
12 {
13 return Message(sourceLineNumbers, Ids.DeprecatedPerfCounterElement, "The PerfCounter element has been deprecated. Please use the PerformanceCounter element instead.");
14 }
15
16 public static Message RequiredAttributeForWindowsXP(SourceLineNumber sourceLineNumbers, string elementName, string attributeName)
17 {
18 return Message(sourceLineNumbers, Ids.RequiredAttributeForWindowsXP, "The {0}/@{1} attribute must be specified to successfully install on Windows XP. You can ignore this warning if this installation does not install on Windows XP.", elementName, attributeName);
19 }
20
21 private static Message Message(SourceLineNumber sourceLineNumber, Ids id, string format, params object[] args)
22 {
23 return new Message(sourceLineNumber, MessageLevel.Warning, (int)id, format, args);
24 }
25
26 private static Message Message(SourceLineNumber sourceLineNumber, Ids id, ResourceManager resourceManager, string resourceName, params object[] args)
27 {
28 return new Message(sourceLineNumber, MessageLevel.Warning, (int)id, resourceManager, resourceName, args);
29 }
30
31 public enum Ids
32 {
33 DeprecatedPerfCounterElement = 5153,
34 RequiredAttributeForWindowsXP = 5154,
35 }
36 }
37}
diff --git a/src/ext/Util/wixext/UtilWindowsInstallerBackendExtension.cs b/src/ext/Util/wixext/UtilWindowsInstallerBackendExtension.cs
new file mode 100644
index 00000000..bca7c700
--- /dev/null
+++ b/src/ext/Util/wixext/UtilWindowsInstallerBackendExtension.cs
@@ -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
3namespace WixToolset.Util
4{
5 using System.Collections.Generic;
6 using WixToolset.Data.WindowsInstaller;
7 using WixToolset.Extensibility;
8
9 public class UtilWindowsInstallerBackendBinderExtension : BaseWindowsInstallerBackendBinderExtension
10 {
11 public override IReadOnlyCollection<TableDefinition> TableDefinitions => UtilTableDefinitions.All;
12 }
13}
diff --git a/src/ext/Util/wixext/WixToolset.Util.wixext.csproj b/src/ext/Util/wixext/WixToolset.Util.wixext.csproj
new file mode 100644
index 00000000..10fc569e
--- /dev/null
+++ b/src/ext/Util/wixext/WixToolset.Util.wixext.csproj
@@ -0,0 +1,31 @@
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 Sdk="Microsoft.NET.Sdk">
5 <PropertyGroup>
6 <TargetFramework>netstandard2.0</TargetFramework>
7 <RootNamespace>WixToolset.Util</RootNamespace>
8 <Description>WiX Toolset Utility Extension</Description>
9 <Title>WiX Toolset Util Extension</Title>
10 <DebugType>embedded</DebugType>
11 <IncludeSymbols>true</IncludeSymbols>
12 </PropertyGroup>
13
14 <ItemGroup>
15 <EmbeddedResource Include="$(OutputPath)..\util.wixlib" />
16 </ItemGroup>
17
18 <ItemGroup>
19 <ProjectReference Include="..\wixlib\util.wixproj" ReferenceOutputAssembly="false" Condition=" '$(NCrunch)'=='' " />
20 </ItemGroup>
21
22 <ItemGroup>
23 <PackageReference Include="WixToolset.Data" Version="4.0.*" />
24 <PackageReference Include="WixToolset.Extensibility" Version="4.0.*" />
25 </ItemGroup>
26
27 <ItemGroup>
28 <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="all" />
29 <PackageReference Include="Nerdbank.GitVersioning" Version="3.3.37" PrivateAssets="All" />
30 </ItemGroup>
31</Project>
diff --git a/src/ext/Util/wixext/WixToolset.Util.wixext.nuspec b/src/ext/Util/wixext/WixToolset.Util.wixext.nuspec
new file mode 100644
index 00000000..ba3eaade
--- /dev/null
+++ b/src/ext/Util/wixext/WixToolset.Util.wixext.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$.targets" target="build" />
18
19 <file src="netstandard2.0\$id$.dll" target="tools" />
20
21 <file src="ARM64\*.pdb" target="pdbs\ARM64" />
22 <file src="x86\*.pdb" target="pdbs\x86" />
23 <file src="x64\*.pdb" target="pdbs\x64" />
24 </files>
25</package>
diff --git a/src/ext/Util/wixext/WixToolset.Util.wixext.targets b/src/ext/Util/wixext/WixToolset.Util.wixext.targets
new file mode 100644
index 00000000..64dff429
--- /dev/null
+++ b/src/ext/Util/wixext/WixToolset.Util.wixext.targets
@@ -0,0 +1,11 @@
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 <PropertyGroup>
6 <WixToolsetUtilWixextPath Condition=" '$(WixToolsetUtilWixextPath)' == '' ">$(MSBuildThisFileDirectory)..\tools\WixToolset.Util.wixext.dll</WixToolsetUtilWixextPath>
7 </PropertyGroup>
8 <ItemGroup>
9 <WixExtension Include="$(WixToolsetUtilWixextPath)" />
10 </ItemGroup>
11</Project>
diff --git a/src/ext/Util/wixext/WixToolset.Util.wixext.v3.ncrunchproject b/src/ext/Util/wixext/WixToolset.Util.wixext.v3.ncrunchproject
new file mode 100644
index 00000000..d75e7ab3
--- /dev/null
+++ b/src/ext/Util/wixext/WixToolset.Util.wixext.v3.ncrunchproject
@@ -0,0 +1,7 @@
1<ProjectConfiguration>
2 <Settings>
3 <AdditionalFilesToIncludeForProject>
4 <Value>..\..\build\Debug\util.wixlib</Value>
5 </AdditionalFilesToIncludeForProject>
6 </Settings>
7</ProjectConfiguration> \ No newline at end of file
diff --git a/src/ext/Util/wixlib/UtilBundleExtension_Platform.wxi b/src/ext/Util/wixlib/UtilBundleExtension_Platform.wxi
new file mode 100644
index 00000000..379c8f57
--- /dev/null
+++ b/src/ext/Util/wixlib/UtilBundleExtension_Platform.wxi
@@ -0,0 +1,10 @@
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<Include xmlns="http://wixtoolset.org/schemas/v4/wxs">
5 <?include caDecor.wxi ?>
6
7 <Fragment>
8 <BundleExtension Id="$(var.Prefix)UtilBundleExtension$(var.Suffix)" SourceFile="!(bindpath.$(var.platform))utilbe.dll" Name="$(var.Prefix)UtilBundleExtension$(var.Suffix)\utilbe.dll" />
9 </Fragment>
10</Include>
diff --git a/src/ext/Util/wixlib/UtilBundleExtension_arm64.wxs b/src/ext/Util/wixlib/UtilBundleExtension_arm64.wxs
new file mode 100644
index 00000000..b17be031
--- /dev/null
+++ b/src/ext/Util/wixlib/UtilBundleExtension_arm64.wxs
@@ -0,0 +1,7 @@
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<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
5 <?define platform=arm64 ?>
6 <?include UtilBundleExtension_Platform.wxi ?>
7</Wix>
diff --git a/src/ext/Util/wixlib/UtilBundleExtension_x64.wxs b/src/ext/Util/wixlib/UtilBundleExtension_x64.wxs
new file mode 100644
index 00000000..96c85a5b
--- /dev/null
+++ b/src/ext/Util/wixlib/UtilBundleExtension_x64.wxs
@@ -0,0 +1,7 @@
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<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
5 <?define platform=x64 ?>
6 <?include UtilBundleExtension_Platform.wxi ?>
7</Wix>
diff --git a/src/ext/Util/wixlib/UtilBundleExtension_x86.wxs b/src/ext/Util/wixlib/UtilBundleExtension_x86.wxs
new file mode 100644
index 00000000..3b458687
--- /dev/null
+++ b/src/ext/Util/wixlib/UtilBundleExtension_x86.wxs
@@ -0,0 +1,7 @@
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<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
5 <?define platform=x86 ?>
6 <?include UtilBundleExtension_Platform.wxi ?>
7</Wix>
diff --git a/src/ext/Util/wixlib/UtilExtension.wxs b/src/ext/Util/wixlib/UtilExtension.wxs
new file mode 100644
index 00000000..0f445ab4
--- /dev/null
+++ b/src/ext/Util/wixlib/UtilExtension.wxs
@@ -0,0 +1,64 @@
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<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
5 <?include caDecor.wxi ?>
6 <?include caerr.wxi ?>
7
8 <Fragment>
9 <UI Id="ConfigureUsersErrorText">
10 <Error Id="$(var.msierrUSRFailedUserCreate)" Message="!(loc.msierrUSRFailedUserCreate)" />
11 <Error Id="$(var.msierrUSRFailedUserCreatePswd)" Message="!(loc.msierrUSRFailedUserCreatePswd)" />
12 <Error Id="$(var.msierrUSRFailedUserGroupAdd)" Message="!(loc.msierrUSRFailedUserGroupAdd)" />
13 <Error Id="$(var.msierrUSRFailedGrantLogonAsService)" Message="Failed to grant 'logon as service' rights to user. ([2] [3] [4] [5])" />
14 <Error Id="$(var.msierrUSRFailedUserCreateExists)" Message="!(loc.msierrUSRFailedUserCreateExists)" />
15 </UI>
16 </Fragment>
17
18 <Fragment>
19 <UI Id="ConfigureSmbErrorsText">
20 <Error Id="$(var.msierrSMBFailedCreate)" Message="!(loc.msierrSMBFailedCreate)" />
21 <Error Id="$(var.msierrSMBFailedDrop)" Message="!(loc.msierrSMBFailedDrop)" />
22 </UI>
23 </Fragment>
24
25 <Fragment>
26 <UI Id="PerCounterDataErrorsText">
27 <Error Id="$(var.msierrInstallPerfCounterData)" Message="!(loc.msierrInstallPerfCounterData)" />
28 <Error Id="$(var.msierrUninstallPerfCounterData)" Message="!(loc.msierrUninstallPerfCounterData)" />
29 </UI>
30 </Fragment>
31
32 <Fragment>
33 <UI Id="ConfigurePerfmonErrorsText">
34 <Error Id="$(var.msierrPERFMONFailedRegisterDLL)" Message="!(loc.msierrPERFMONFailedRegisterDLL)" />
35 <Error Id="$(var.msierrPERFMONFailedUnregisterDLL)" Message="!(loc.msierrPERFMONFailedUnregisterDLL)" />
36 </UI>
37 </Fragment>
38
39 <Fragment>
40 <UI Id="SecureObjectsErrors">
41 <Error Id="$(var.msierrSecureObjectsFailedCreateSD)" Message="!(loc.msierrSecureObjectsFailedCreateSD)" />
42 <Error Id="$(var.msierrSecureObjectsFailedSet)" Message="!(loc.msierrSecureObjectsFailedSet)" />
43 <Error Id="$(var.msierrSecureObjectsUnknownType)" Message="!(loc.msierrSecureObjectsUnknownType)" />
44 </UI>
45 </Fragment>
46
47 <Fragment>
48 <UI Id="XmlFileErrorsText">
49 <Error Id="$(var.msierrXmlFileFailedRead)" Message="!(loc.msierrXmlFileFailedRead)" />
50 <Error Id="$(var.msierrXmlFileFailedOpen)" Message="!(loc.msierrXmlFileFailedOpen)" />
51 <Error Id="$(var.msierrXmlFileFailedSelect)" Message="!(loc.msierrXmlFileFailedSelect)" />
52 <Error Id="$(var.msierrXmlFileFailedSave)" Message="!(loc.msierrXmlFileFailedSave)" />
53 </UI>
54 </Fragment>
55
56 <Fragment>
57 <UI Id="XmlConfigErrorsText">
58 <Error Id="$(var.msierrXmlConfigFailedRead)" Message="!(loc.msierrXmlConfigFailedRead)" />
59 <Error Id="$(var.msierrXmlConfigFailedOpen)" Message="!(loc.msierrXmlConfigFailedOpen)" />
60 <Error Id="$(var.msierrXmlConfigFailedSelect)" Message="!(loc.msierrXmlConfigFailedSelect)" />
61 <Error Id="$(var.msierrXmlConfigFailedSave)" Message="!(loc.msierrXmlConfigFailedSave)" />
62 </UI>
63 </Fragment>
64</Wix>
diff --git a/src/ext/Util/wixlib/UtilExtension_Platform.wxi b/src/ext/Util/wixlib/UtilExtension_Platform.wxi
new file mode 100644
index 00000000..913c01b9
--- /dev/null
+++ b/src/ext/Util/wixlib/UtilExtension_Platform.wxi
@@ -0,0 +1,360 @@
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<Include xmlns="http://wixtoolset.org/schemas/v4/wxs">
5 <?include caDecor.wxi ?>
6
7 <Fragment>
8 <CustomAction Id="$(var.Prefix)FailWhenDeferred$(var.Suffix)" DllEntry="WixFailWhenDeferred" Execute="deferred" Return="check" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
9
10 <InstallExecuteSequence>
11 <Custom Action="$(var.Prefix)FailWhenDeferred$(var.Suffix)" Before="InstallFinalize" Overridable="yes" Condition="WIXFAILWHENDEFERRED=1 AND VersionNT &gt; 400" />
12 </InstallExecuteSequence>
13 </Fragment>
14
15 <Fragment>
16 <CustomAction Id="$(var.Prefix)WaitForEvent$(var.Suffix)" DllEntry="WixWaitForEvent" Execute="immediate" Return="check" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
17
18 <InstallExecuteSequence>
19 <Custom Action="$(var.Prefix)WaitForEvent$(var.Suffix)" Before="InstallFinalize" Overridable="yes" />
20 </InstallExecuteSequence>
21 </Fragment>
22
23 <Fragment>
24 <CustomAction Id="$(var.Prefix)WaitForEventDeferred$(var.Suffix)" DllEntry="WixWaitForEvent" Execute="deferred" Return="check" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
25
26 <InstallExecuteSequence>
27 <Custom Action="$(var.Prefix)WaitForEventDeferred$(var.Suffix)" After="InstallInitialize" Overridable="yes" />
28 </InstallExecuteSequence>
29 </Fragment>
30
31 <Fragment>
32 <CustomAction Id="$(var.Prefix)ExitEarlyWithSuccess$(var.Suffix)" DllEntry="WixExitEarlyWithSuccess" Execute="immediate" Return="check" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
33
34 <InstallExecuteSequence>
35 <Custom Action="$(var.Prefix)ExitEarlyWithSuccess$(var.Suffix)" After="FindRelatedProducts" Overridable="yes" Condition="NEWERVERSIONDETECTED AND VersionNT &gt; 400" />
36 </InstallExecuteSequence>
37 </Fragment>
38
39 <Fragment>
40 <CustomAction Id="$(var.Prefix)RemoveFoldersEx$(var.Suffix)" DllEntry="WixRemoveFoldersEx" Execute="immediate" Return="ignore" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
41
42 <InstallExecuteSequence>
43 <Custom Action="$(var.Prefix)RemoveFoldersEx$(var.Suffix)" Before="CostInitialize" />
44 </InstallExecuteSequence>
45 </Fragment>
46
47 <Fragment>
48 <CustomAction Id="$(var.Prefix)RemoveRegistryKeysEx$(var.Suffix)" DllEntry="WixRemoveRegistryKeysEx" Execute="immediate" Return="ignore" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
49
50 <InstallExecuteSequence>
51 <Custom Action="$(var.Prefix)RemoveRegistryKeysEx$(var.Suffix)" Before="RemoveRegistryValues" />
52 </InstallExecuteSequence>
53 </Fragment>
54
55 <Fragment>
56 <CustomAction Id="$(var.Prefix)BroadcastSettingChange$(var.Suffix)" DllEntry="WixBroadcastSettingChange" Execute="immediate" Return="ignore" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
57
58 <InstallExecuteSequence>
59 <Custom Action="$(var.Prefix)BroadcastSettingChange$(var.Suffix)" After="InstallFinalize" Overridable="yes" />
60 </InstallExecuteSequence>
61 </Fragment>
62
63 <Fragment>
64 <CustomAction Id="$(var.Prefix)BroadcastEnvironmentChange$(var.Suffix)" DllEntry="WixBroadcastEnvironmentChange" Execute="immediate" Return="ignore" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
65
66 <InstallExecuteSequence>
67 <Custom Action="$(var.Prefix)BroadcastEnvironmentChange$(var.Suffix)" After="InstallFinalize" Overridable="yes" />
68 </InstallExecuteSequence>
69 </Fragment>
70
71 <!-- ShellExec custom actions (for when only one is needed; multiple executions need their own IDs) -->
72 <Fragment>
73 <PropertyRef Id="WixShellExecBinaryId" />
74 <CustomAction Id="$(var.Prefix)ShellExecBinary$(var.Suffix)" DllEntry="WixShellExecBinary" Execute="immediate" Return="check" Impersonate="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
75 </Fragment>
76
77 <Fragment>
78 <PropertyRef Id="WixShellExecTarget" />
79 <CustomAction Id="$(var.Prefix)ShellExec$(var.Suffix)" DllEntry="WixShellExec" Execute="immediate" Return="check" Impersonate="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
80 </Fragment>
81
82 <Fragment>
83 <PropertyRef Id="WixUnelevatedShellExecTarget" />
84 <CustomAction Id="$(var.Prefix)UnelevatedShellExec$(var.Suffix)" DllEntry="WixUnelevatedShellExec" Execute="immediate" Return="check" Impersonate="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
85 </Fragment>
86
87 <Fragment>
88 <PropertyRef Id="WixQuietExecCmdLine" />
89 <CustomAction Id="$(var.Prefix)QuietExec$(var.Suffix)" DllEntry="WixQuietExec" Execute="immediate" Return="check" Impersonate="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
90 </Fragment>
91
92 <Fragment>
93 <PropertyRef Id="WixQuietExec64CmdLine" />
94 <CustomAction Id="$(var.Prefix)QuietExec64$(var.Suffix)" DllEntry="WixQuietExec64" Execute="immediate" Return="check" Impersonate="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
95 </Fragment>
96
97 <!-- SilentExec custom actions differ from QtExec in that they do not log the commandline or output of the exe -->
98 <Fragment>
99 <PropertyRef Id="WixSilentExecCmdLine" />
100 <CustomAction Id="$(var.Prefix)SilentExec$(var.Suffix)" DllEntry="WixSilentExec" Execute="immediate" Return="check" Impersonate="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
101 </Fragment>
102
103 <Fragment>
104 <PropertyRef Id="WixSilentExec64CmdLine" />
105 <CustomAction Id="$(var.Prefix)SilentExec64$(var.Suffix)" DllEntry="WixSilentExec64" Execute="immediate" Return="check" Impersonate="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
106 </Fragment>
107
108 <Fragment>
109 <CustomAction Id="$(var.Prefix)CheckRebootRequired$(var.Suffix)" DllEntry="WixCheckRebootRequired" Execute="immediate" Return="ignore" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
110
111 <InstallExecuteSequence>
112 <!-- Condition this so it runs on install and MMode, but not uninstall -->
113 <Custom Action="$(var.Prefix)CheckRebootRequired$(var.Suffix)" After="InstallFinalize" Overridable="yes" Condition="NOT REMOVE~=&quot;ALL&quot; AND VersionNT &gt; 400" />
114 </InstallExecuteSequence>
115 </Fragment>
116
117 <Fragment>
118 <CustomAction Id="$(var.Prefix)CloseApplications$(var.Suffix)" DllEntry="WixCloseApplications" Execute="immediate" Return="check" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
119 <CustomAction Id="$(var.Prefix)CloseApplicationsDeferred$(var.Suffix)" DllEntry="WixCloseApplicationsDeferred" Impersonate="no" Execute="deferred" Return="check" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
120 <CustomActionRef Id="$(var.Prefix)CheckRebootRequired$(var.Suffix)" />
121
122 <InstallExecuteSequence>
123 <Custom Action="$(var.Prefix)CloseApplications$(var.Suffix)" Before="InstallFiles" Overridable="yes" Condition="VersionNT &gt; 400" />
124 </InstallExecuteSequence>
125 </Fragment>
126
127 <Fragment>
128 <CustomAction Id="$(var.Prefix)RegisterRestartResources$(var.Suffix)" DllEntry="WixRegisterRestartResources$(var.Suffix)" Execute="immediate" Return="check" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
129
130 <InstallExecuteSequence>
131 <Custom Action="$(var.Prefix)RegisterRestartResources$(var.Suffix)" Before="InstallValidate" Overridable="yes" />
132 </InstallExecuteSequence>
133 </Fragment>
134
135 <Fragment>
136 <UIRef Id="ConfigureUsersErrorText" />
137
138 <CustomAction Id="$(var.Prefix)ConfigureUsers$(var.Suffix)" DllEntry="ConfigureUsers" Execute="immediate" Return="check" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
139 <CustomAction Id="$(var.Prefix)CreateUser$(var.Suffix)" DllEntry="CreateUser" Impersonate="no" Execute="deferred" Return="check" HideTarget="yes" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
140 <CustomAction Id="$(var.Prefix)CreateUserRollback$(var.Suffix)" DllEntry="CreateUserRollback" Impersonate="no" Execute="rollback" Return="check" HideTarget="yes" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
141 <!-- RemoveUser is a type commit action because it is not possible to rollback the removal of a user -->
142 <CustomAction Id="$(var.Prefix)RemoveUser$(var.Suffix)" DllEntry="RemoveUser" Impersonate="no" Execute="commit" Return="ignore" HideTarget="yes" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
143
144 <InstallExecuteSequence>
145 <Custom Action="$(var.Prefix)ConfigureUsers$(var.Suffix)" Before="InstallFiles" Overridable="yes" Condition="VersionNT &gt; 400" />
146 </InstallExecuteSequence>
147 </Fragment>
148
149 <Fragment>
150 <UIRef Id="ConfigureSmbErrorsText" />
151
152 <CustomAction Id="$(var.Prefix)ConfigureSmbInstall$(var.Suffix)" DllEntry="ConfigureSmbInstall" Execute="immediate" Return="check" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
153 <CustomAction Id="$(var.Prefix)ConfigureSmbUninstall$(var.Suffix)" DllEntry="ConfigureSmbUninstall" Execute="immediate" Return="check" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
154 <CustomAction Id="$(var.Prefix)CreateSmb$(var.Suffix)" DllEntry="CreateSmb" Impersonate="no" Execute="deferred" Return="check" HideTarget="yes" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
155 <CustomAction Id="$(var.Prefix)CreateSmbRollback$(var.Suffix)" DllEntry="DropSmb" Impersonate="no" Execute="rollback" Return="ignore" HideTarget="yes" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
156 <CustomAction Id="$(var.Prefix)DropSmb$(var.Suffix)" DllEntry="DropSmb" Impersonate="no" Execute="deferred" Return="check" HideTarget="yes" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
157 <CustomAction Id="$(var.Prefix)DropSmbRollback$(var.Suffix)" DllEntry="CreateSmb" Impersonate="no" Execute="rollback" Return="ignore" HideTarget="yes" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
158
159 <InstallExecuteSequence>
160 <Custom Action="$(var.Prefix)ConfigureSmbInstall$(var.Suffix)" After="InstallFiles" Overridable="yes" Condition="VersionNT &gt; 400" />
161 <Custom Action="$(var.Prefix)ConfigureSmbUninstall$(var.Suffix)" After="RemoveFiles" Overridable="yes" Condition="VersionNT &gt; 400" />
162 </InstallExecuteSequence>
163 </Fragment>
164
165 <Fragment>
166 <UIRef Id="PerCounterDataErrorsText" />
167
168 <CustomAction Id="$(var.Prefix)InstallPerfCounterData$(var.Suffix)" DllEntry="InstallPerfCounterData" Execute="immediate" Return="check" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
169 <CustomAction Id="$(var.Prefix)UninstallPerfCounterData$(var.Suffix)" DllEntry="UninstallPerfCounterData" Execute="immediate" Return="check" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
170 <CustomAction Id="$(var.Prefix)RegisterPerfCounterData$(var.Suffix)" DllEntry="RegisterPerfCounterData" Impersonate="no" Execute="deferred" Return="check" HideTarget="yes" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
171 <CustomAction Id="$(var.Prefix)UnregisterPerfCounterData$(var.Suffix)" DllEntry="UnregisterPerfCounterData" Impersonate="no" Execute="deferred" Return="check" HideTarget="yes" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
172 <CustomAction Id="$(var.Prefix)RollbackRegisterPerfCounterData$(var.Suffix)" DllEntry="UnregisterPerfCounterData" Impersonate="no" Execute="rollback" Return="check" HideTarget="yes" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
173 <CustomAction Id="$(var.Prefix)RollbackUnregisterPerfCounterData$(var.Suffix)" DllEntry="RegisterPerfCounterData" Impersonate="no" Execute="rollback" Return="check" HideTarget="yes" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
174
175 <InstallExecuteSequence>
176 <Custom Action="$(var.Prefix)InstallPerfCounterData$(var.Suffix)" After="WriteRegistryValues" Overridable="yes" Condition="VersionNT &gt; 400" />
177 <Custom Action="$(var.Prefix)UninstallPerfCounterData$(var.Suffix)" Before="RemoveRegistryValues" Overridable="yes" Condition="VersionNT &gt; 400" />
178 </InstallExecuteSequence>
179 </Fragment>
180
181 <Fragment>
182 <UIRef Id="ConfigurePerfmonErrorsText" />
183
184 <CustomAction Id="$(var.Prefix)ConfigurePerfmonInstall$(var.Suffix)" DllEntry="ConfigurePerfmonInstall" Execute="immediate" Return="check" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
185 <CustomAction Id="$(var.Prefix)ConfigurePerfmonUninstall$(var.Suffix)" DllEntry="ConfigurePerfmonUninstall" Execute="immediate" Return="check" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
186 <CustomAction Id="$(var.Prefix)RegisterPerfmon$(var.Suffix)" DllEntry="RegisterPerfmon" Impersonate="no" Execute="deferred" Return="check" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
187 <CustomAction Id="$(var.Prefix)UnregisterPerfmon$(var.Suffix)" DllEntry="UnregisterPerfmon" Impersonate="no" Execute="deferred" Return="check" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
188 <CustomAction Id="$(var.Prefix)RollbackRegisterPerfmon$(var.Suffix)" DllEntry="UnregisterPerfmon" Impersonate="no" Execute="rollback" Return="check" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
189 <CustomAction Id="$(var.Prefix)RollbackUnregisterPerfmon$(var.Suffix)" DllEntry="RegisterPerfmon" Impersonate="no" Execute="rollback" Return="check" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
190
191 <InstallExecuteSequence>
192 <Custom Action="$(var.Prefix)ConfigurePerfmonInstall$(var.Suffix)" After="WriteRegistryValues" Overridable="yes" Condition="VersionNT &gt; 400" />
193 <Custom Action="$(var.Prefix)ConfigurePerfmonUninstall$(var.Suffix)" Before="RemoveRegistryValues" Overridable="yes" Condition="VersionNT &gt; 400" />
194 </InstallExecuteSequence>
195 </Fragment>
196
197 <Fragment>
198 <CustomAction Id="$(var.Prefix)ConfigurePerfmonManifestRegister$(var.Suffix)" DllEntry="ConfigurePerfmonManifestRegister" Execute="immediate" Return="check" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
199 <CustomAction Id="$(var.Prefix)ConfigurePerfmonManifestUnregister$(var.Suffix)" DllEntry="ConfigurePerfmonManifestUnregister" Execute="immediate" Return="check" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
200 <CustomAction Id="$(var.Prefix)RegisterPerfmonManifest$(var.Suffix)" DllEntry="WixQuietExec" Impersonate="no" Execute="deferred" Return="check" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
201 <CustomAction Id="$(var.Prefix)UnregisterPerfmonManifest$(var.Suffix)" DllEntry="WixQuietExec" Impersonate="no" Execute="deferred" Return="ignore" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
202 <CustomAction Id="$(var.Prefix)RollbackRegisterPerfmonManifest$(var.Suffix)" DllEntry="WixQuietExec" Impersonate="no" Execute="rollback" Return="ignore" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
203 <CustomAction Id="$(var.Prefix)RollbackUnregisterPerfmonManifest$(var.Suffix)" DllEntry="WixQuietExec" Impersonate="no" Execute="rollback" Return="check" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
204
205 <InstallExecuteSequence>
206 <Custom Action="$(var.Prefix)ConfigurePerfmonManifestRegister$(var.Suffix)" After="InstallFiles" Overridable="yes" Condition="VersionNT &gt; 400" />
207 <Custom Action="$(var.Prefix)ConfigurePerfmonManifestUnregister$(var.Suffix)" After="RemoveRegistryValues" Overridable="yes" Condition="VersionNT &gt; 400" />
208 </InstallExecuteSequence>
209 </Fragment>
210
211 <Fragment>
212 <CustomAction Id="$(var.Prefix)ConfigureEventManifestRegister$(var.Suffix)" DllEntry="ConfigureEventManifestRegister" Execute="immediate" Return="check" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
213 <CustomAction Id="$(var.Prefix)ConfigureEventManifestUnregister$(var.Suffix)" DllEntry="ConfigureEventManifestUnregister" Execute="immediate" Return="check" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
214 <CustomAction Id="$(var.Prefix)RegisterEventManifest$(var.Suffix)" DllEntry="WixQuietExec" Impersonate="no" Execute="deferred" Return="check" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
215 <CustomAction Id="$(var.Prefix)UnregisterEventManifest$(var.Suffix)" DllEntry="WixQuietExec" Impersonate="no" Execute="deferred" Return="ignore" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
216 <CustomAction Id="$(var.Prefix)RollbackRegisterEventManifest$(var.Suffix)" DllEntry="WixQuietExec" Impersonate="no" Execute="rollback" Return="ignore" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
217 <CustomAction Id="$(var.Prefix)RollbackUnregisterEventManifest$(var.Suffix)" DllEntry="WixQuietExec" Impersonate="no" Execute="rollback" Return="check" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
218
219 <InstallExecuteSequence>
220 <Custom Action="$(var.Prefix)ConfigureEventManifestRegister$(var.Suffix)" After="$(var.Prefix)SchedXmlFile$(var.Suffix)" Overridable="yes" Condition="VersionNT &gt; 400" />
221 <Custom Action="$(var.Prefix)ConfigureEventManifestUnregister$(var.Suffix)" After="RemoveRegistryValues" Overridable="yes" Condition="VersionNT &gt; 400" />
222 </InstallExecuteSequence>
223 </Fragment>
224
225 <Fragment>
226 <CustomAction Id="$(var.Prefix)SchedServiceConfig$(var.Suffix)" DllEntry="SchedServiceConfig" Execute="immediate" Return="check" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
227 <CustomAction Id="$(var.Prefix)ExecServiceConfig$(var.Suffix)" DllEntry="ExecServiceConfig" Execute="deferred" Impersonate="no" Return="check" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
228 <CustomAction Id="$(var.Prefix)RollbackServiceConfig$(var.Suffix)" DllEntry="RollbackServiceConfig" Execute="rollback" Impersonate="no" Return="check" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
229
230 <InstallExecuteSequence>
231 <!-- Condition this so it runs on install and MMode, but not uninstall -->
232 <Custom Action="$(var.Prefix)SchedServiceConfig$(var.Suffix)" After="InstallServices" Overridable="yes" Condition="NOT REMOVE~=&quot;ALL&quot; AND VersionNT &gt; 400" />
233 </InstallExecuteSequence>
234 </Fragment>
235
236 <Fragment>
237 <CustomAction Id="$(var.Prefix)TouchFileDuringInstall$(var.Suffix)" DllEntry="WixTouchFileDuringInstall" Execute="immediate" Return="check" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
238 <CustomAction Id="$(var.Prefix)TouchFileDuringUninstall$(var.Suffix)" DllEntry="WixTouchFileDuringUninstall" Execute="immediate" Return="check" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
239 <CustomAction Id="$(var.Prefix)ExecuteTouchFile$(var.Suffix)" DllEntry="WixExecuteTouchFile" Execute="deferred" Impersonate="no" Return="check" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
240 <CustomAction Id="$(var.Prefix)RollbackTouchFile$(var.Suffix)" DllEntry="WixExecuteTouchFile" Execute="rollback" Impersonate="no" Return="check" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
241
242 <InstallExecuteSequence>
243 <Custom Action="$(var.Prefix)TouchFileDuringUninstall" Before="RemoveFiles" Overridable="yes" />
244 <Custom Action="$(var.Prefix)TouchFileDuringInstall" After="InstallFiles" Overridable="yes" />
245 </InstallExecuteSequence>
246 </Fragment>
247
248 <Fragment>
249 <UIRef Id="XmlFileErrorsText" />
250
251 <CustomAction Id="$(var.Prefix)SchedXmlFile$(var.Suffix)" DllEntry="SchedXmlFile" Execute="immediate" Return="check" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
252 <CustomAction Id="$(var.Prefix)ExecXmlFile$(var.Suffix)" DllEntry="ExecXmlFile" Execute="deferred" Impersonate="no" Return="check" HideTarget="yes" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
253 <CustomAction Id="$(var.Prefix)ExecXmlFileRollback$(var.Suffix)" DllEntry="ExecXmlFileRollback" Execute="rollback" Impersonate="no" Return="check" HideTarget="yes" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
254
255 <InstallExecuteSequence>
256 <Custom Action="$(var.Prefix)SchedXmlFile$(var.Suffix)" After="DuplicateFiles" Overridable="yes" Condition="VersionNT &gt; 400" />
257 </InstallExecuteSequence>
258 </Fragment>
259
260 <Fragment>
261 <UIRef Id="XmlConfigErrorsText" />
262
263 <CustomAction Id="$(var.Prefix)SchedXmlConfig$(var.Suffix)" DllEntry="SchedXmlConfig" Execute="immediate" Return="check" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
264 <CustomAction Id="$(var.Prefix)ExecXmlConfig$(var.Suffix)" DllEntry="ExecXmlConfig" Execute="deferred" Impersonate="no" Return="check" HideTarget="yes" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
265 <CustomAction Id="$(var.Prefix)ExecXmlConfigRollback$(var.Suffix)" DllEntry="ExecXmlConfigRollback" Execute="rollback" Impersonate="no" Return="check" HideTarget="yes" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
266
267 <InstallExecuteSequence>
268 <Custom Action="$(var.Prefix)SchedXmlConfig$(var.Suffix)" After="DuplicateFiles" Overridable="yes" Condition="VersionNT &gt; 400" />
269 </InstallExecuteSequence>
270 </Fragment>
271
272 <Fragment>
273 <CustomAction Id="$(var.Prefix)SchedInternetShortcuts$(var.Suffix)" DllEntry="WixSchedInternetShortcuts" Execute="immediate" Return="check" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
274 <CustomAction Id="$(var.Prefix)RollbackInternetShortcuts$(var.Suffix)" DllEntry="WixRollbackInternetShortcuts" Impersonate="no" Execute="rollback" Return="check" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
275 <CustomAction Id="$(var.Prefix)CreateInternetShortcuts$(var.Suffix)" DllEntry="WixCreateInternetShortcuts" Impersonate="no" Execute="deferred" Return="check" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
276
277 <InstallExecuteSequence>
278 <Custom Action="$(var.Prefix)SchedInternetShortcuts$(var.Suffix)" Before="RemoveFolders" Overridable="yes" Condition="VersionNT &gt; 400" />
279 <Custom Action="$(var.Prefix)RollbackInternetShortcuts$(var.Suffix)" Before="$(var.Prefix)CreateInternetShortcuts$(var.Suffix)" Overridable="yes" Condition="VersionNT &gt; 400" />
280 <Custom Action="$(var.Prefix)CreateInternetShortcuts$(var.Suffix)" After="CreateShortcuts" Overridable="yes" Condition="VersionNT &gt; 400" />
281 </InstallExecuteSequence>
282 </Fragment>
283
284 <Fragment>
285 <UIRef Id="SecureObjectsErrors" />
286
287 <CustomAction Id="$(var.Prefix)SchedSecureObjects$(var.Suffix)" DllEntry="SchedSecureObjects" Execute="immediate" Return="check" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
288 <CustomAction Id="$(var.Prefix)SchedSecureObjectsRollback$(var.Suffix)" DllEntry="SchedSecureObjectsRollback" Execute="immediate" Return="check" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
289 <CustomAction Id="$(var.Prefix)ExecSecureObjects$(var.Suffix)" DllEntry="ExecSecureObjects" Execute="deferred" Impersonate="no" Return="check" HideTarget="yes" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
290 <CustomAction Id="$(var.Prefix)ExecSecureObjectsRollback$(var.Suffix)" DllEntry="ExecSecureObjectsRollback" Execute="rollback" Impersonate="no" Return="check" HideTarget="yes" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
291
292 <InstallExecuteSequence>
293 <!-- Condition this so it runs on install and MMode, but not uninstall -->
294 <Custom Action="$(var.Prefix)SchedSecureObjects$(var.Suffix)" After="InstallServices" Overridable="yes" Condition="NOT REMOVE~=&quot;ALL&quot; AND VersionNT &gt; 400" />
295 <Custom Action="$(var.Prefix)SchedSecureObjectsRollback$(var.Suffix)" After="UnpublishFeatures" Overridable="yes" Condition="VersionNT &gt; 400" />
296 </InstallExecuteSequence>
297 </Fragment>
298
299 <Fragment>
300 <CustomAction Id="$(var.Prefix)SchedFormatFiles$(var.Suffix)" DllEntry="WixSchedFormatFiles" Execute="immediate" Return="check" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
301 <CustomAction Id="$(var.Prefix)ExecFormatFiles$(var.Suffix)" DllEntry="WixExecFormatFiles" Execute="deferred" Impersonate="no" Return="check" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
302 <CustomAction Id="$(var.Prefix)RollbackFormatFiles$(var.Suffix)" DllEntry="WixExecFormatFiles" Execute="rollback" Impersonate="no" Return="check" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
303
304 <InstallExecuteSequence>
305 <Custom Action="$(var.Prefix)SchedFormatFiles$(var.Suffix)" After="InstallFiles" />
306 </InstallExecuteSequence>
307 </Fragment>
308
309 <Fragment>
310 <CustomAction Id="$(var.Prefix)QueryOsInfo$(var.Suffix)" DllEntry="WixQueryOsInfo" Execute="firstSequence" Return="check" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
311
312 <InstallExecuteSequence>
313 <Custom Action="$(var.Prefix)QueryOsInfo$(var.Suffix)" After="AppSearch" Overridable="yes" Condition="VersionNT &gt; 400 OR (VersionNT = 400 AND ServicePackLevel &gt; 3)" />
314 </InstallExecuteSequence>
315
316 <InstallUISequence>
317 <Custom Action="$(var.Prefix)QueryOsInfo$(var.Suffix)" After="AppSearch" Overridable="yes" Condition="VersionNT &gt; 400 OR (VersionNT = 400 AND ServicePackLevel &gt; 3)" />
318 </InstallUISequence>
319 </Fragment>
320
321 <Fragment>
322 <CustomAction Id="$(var.Prefix)QueryOsDirs$(var.Suffix)" DllEntry="WixQueryOsDirs" Execute="firstSequence" Return="check" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
323
324 <InstallExecuteSequence>
325 <Custom Action="$(var.Prefix)QueryOsDirs$(var.Suffix)" After="AppSearch" Overridable="yes" Condition="VersionNT &gt; 400 OR (VersionNT = 400 AND ServicePackLevel &gt; 3)" />
326 </InstallExecuteSequence>
327
328 <InstallUISequence>
329 <Custom Action="$(var.Prefix)QueryOsDirs$(var.Suffix)" After="AppSearch" Overridable="yes" Condition="VersionNT &gt; 400 OR (VersionNT = 400 AND ServicePackLevel &gt; 3)" />
330 </InstallUISequence>
331 </Fragment>
332
333 <Fragment>
334 <CustomAction Id="$(var.Prefix)QueryOsWellKnownSID$(var.Suffix)" DllEntry="WixQueryOsWellKnownSID" Execute="firstSequence" Return="check" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
335
336 <InstallExecuteSequence>
337 <Custom Action="$(var.Prefix)QueryOsWellKnownSID$(var.Suffix)" After="AppSearch" Overridable="yes" Condition="VersionNT &gt; 400 OR (VersionNT = 400 AND ServicePackLevel &gt; 3)" />
338 </InstallExecuteSequence>
339
340 <InstallUISequence>
341 <Custom Action="$(var.Prefix)QueryOsWellKnownSID$(var.Suffix)" After="AppSearch" Overridable="yes" Condition="VersionNT &gt; 400 OR (VersionNT = 400 AND ServicePackLevel &gt; 3)" />
342 </InstallUISequence>
343 </Fragment>
344
345 <Fragment>
346 <CustomAction Id="$(var.Prefix)QueryOsDriverInfo$(var.Suffix)" DllEntry="WixQueryOsDriverInfo" Execute="firstSequence" Return="check" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
347
348 <InstallExecuteSequence>
349 <Custom Action="$(var.Prefix)QueryOsDriverInfo$(var.Suffix)" After="AppSearch" Overridable="yes" Condition="VersionNT &gt; 400 OR (VersionNT = 400 AND ServicePackLevel &gt; 3)" />
350 </InstallExecuteSequence>
351
352 <InstallUISequence>
353 <Custom Action="$(var.Prefix)QueryOsDriverInfo$(var.Suffix)" After="AppSearch" Overridable="yes" Condition="VersionNT &gt; 400 OR (VersionNT = 400 AND ServicePackLevel &gt; 3)" />
354 </InstallUISequence>
355 </Fragment>
356
357 <Fragment>
358 <Binary Id="$(var.Prefix)UtilCA$(var.Suffix)" SourceFile="!(bindpath.$(var.platform))utilca.dll" />
359 </Fragment>
360</Include>
diff --git a/src/ext/Util/wixlib/UtilExtension_arm64.wxs b/src/ext/Util/wixlib/UtilExtension_arm64.wxs
new file mode 100644
index 00000000..b9dc73b8
--- /dev/null
+++ b/src/ext/Util/wixlib/UtilExtension_arm64.wxs
@@ -0,0 +1,7 @@
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<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
5 <?define platform=arm64 ?>
6 <?include UtilExtension_Platform.wxi ?>
7</Wix>
diff --git a/src/ext/Util/wixlib/UtilExtension_x64.wxs b/src/ext/Util/wixlib/UtilExtension_x64.wxs
new file mode 100644
index 00000000..40cdf306
--- /dev/null
+++ b/src/ext/Util/wixlib/UtilExtension_x64.wxs
@@ -0,0 +1,7 @@
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<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
5 <?define platform=x64 ?>
6 <?include UtilExtension_Platform.wxi ?>
7</Wix>
diff --git a/src/ext/Util/wixlib/UtilExtension_x86.wxs b/src/ext/Util/wixlib/UtilExtension_x86.wxs
new file mode 100644
index 00000000..bd0fa562
--- /dev/null
+++ b/src/ext/Util/wixlib/UtilExtension_x86.wxs
@@ -0,0 +1,7 @@
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<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
5 <?define platform=x86 ?>
6 <?include UtilExtension_Platform.wxi ?>
7</Wix>
diff --git a/src/ext/Util/wixlib/caDecor.wxi b/src/ext/Util/wixlib/caDecor.wxi
new file mode 100644
index 00000000..b1711518
--- /dev/null
+++ b/src/ext/Util/wixlib/caDecor.wxi
@@ -0,0 +1,39 @@
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<Include xmlns="http://wixtoolset.org/schemas/v4/wxs">
5 <?ifdef Prefix ?>
6 <?undef Prefix ?>
7 <?endif?>
8
9 <?define Prefix="Wix4" ?>
10
11 <?ifndef platform ?>
12 <?define platform="x86" ?>
13 <?endif?>
14
15 <?if $(var.platform)="" ?>
16 <?undef platform ?>
17 <?define platform="x86" ?>
18 <?endif?>
19
20 <?ifdef Suffix ?>
21 <?undef Suffix ?>
22 <?endif?>
23
24 <?if $(var.platform)~="x86" ?>
25 <?define Suffix="_X86" ?>
26 <?endif?>
27
28 <?if $(var.platform)~="x64" ?>
29 <?define Suffix="_X64" ?>
30 <?endif?>
31
32 <?if $(var.platform)~="arm" ?>
33 <?define Suffix="_A32" ?>
34 <?endif?>
35
36 <?if $(var.platform)~="arm64" ?>
37 <?define Suffix="_A64" ?>
38 <?endif?>
39</Include>
diff --git a/src/ext/Util/wixlib/caerr.wxi b/src/ext/Util/wixlib/caerr.wxi
new file mode 100644
index 00000000..ff7ec121
--- /dev/null
+++ b/src/ext/Util/wixlib/caerr.wxi
@@ -0,0 +1,96 @@
1<Include xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 <?define msierrSecureObjectsFailedCreateSD = 25520?>
3 <?define msierrSecureObjectsFailedSet = 25521?>
4 <?define msierrSecureObjectsUnknownType = 25522?>
5 <?define msierrXmlFileFailedRead = 25530?>
6 <?define msierrXmlFileFailedOpen = 25531?>
7 <?define msierrXmlFileFailedSelect = 25532?>
8 <?define msierrXmlFileFailedSave = 25533?>
9 <?define msierrXmlConfigFailedRead = 25540?>
10 <?define msierrXmlConfigFailedOpen = 25541?>
11 <?define msierrXmlConfigFailedSelect = 25542?>
12 <?define msierrXmlConfigFailedSave = 25543?>
13 <?define msierrFirewallCannotConnect = 25580?>
14 <?define msierrIISCannotConnect = 26001?>
15 <?define msierrIISFailedReadWebSite = 26002?>
16 <?define msierrIISFailedReadWebDirs = 26003?>
17 <?define msierrIISFailedReadVDirs = 26004?>
18 <?define msierrIISFailedReadFilters = 26005?>
19 <?define msierrIISFailedReadAppPool = 26006?>
20 <?define msierrIISFailedReadMimeMap = 26007?>
21 <?define msierrIISFailedReadProp = 26008?>
22 <?define msierrIISFailedReadWebSvcExt = 26009?>
23 <?define msierrIISFailedReadWebError = 26010?>
24 <?define msierrIISFailedReadHttpHeader = 26011?>
25 <?define msierrIISFailedSchedTransaction = 26031?>
26 <?define msierrIISFailedSchedInstallWebs = 26032?>
27 <?define msierrIISFailedSchedInstallWebDirs = 26033?>
28 <?define msierrIISFailedSchedInstallVDirs = 26034?>
29 <?define msierrIISFailedSchedInstallFilters = 26035?>
30 <?define msierrIISFailedSchedInstallAppPool = 26036?>
31 <?define msierrIISFailedSchedInstallProp = 26037?>
32 <?define msierrIISFailedSchedInstallWebSvcExt = 26038?>
33 <?define msierrIISFailedSchedUninstallWebs = 26051?>
34 <?define msierrIISFailedSchedUninstallWebDirs = 26052?>
35 <?define msierrIISFailedSchedUninstallVDirs = 26053?>
36 <?define msierrIISFailedSchedUninstallFilters = 26054?>
37 <?define msierrIISFailedSchedUninstallAppPool = 26055?>
38 <?define msierrIISFailedSchedUninstallProp = 26056?>
39 <?define msierrIISFailedSchedUninstallWebSvcExt = 26057?>
40 <?define msierrIISFailedStartTransaction = 26101?>
41 <?define msierrIISFailedOpenKey = 26102?>
42 <?define msierrIISFailedCreateKey = 26103?>
43 <?define msierrIISFailedWriteData = 26104?>
44 <?define msierrIISFailedCreateApp = 26105?>
45 <?define msierrIISFailedDeleteKey = 26106?>
46 <?define msierrIISFailedDeleteApp = 26107?>
47 <?define msierrIISFailedDeleteValue = 26108?>
48 <?define msierrIISFailedCommitInUse = 26109?>
49 <?define msierrSQLFailedCreateDatabase = 26201?>
50 <?define msierrSQLFailedDropDatabase = 26202?>
51 <?define msierrSQLFailedConnectDatabase = 26203?>
52 <?define msierrSQLFailedExecString = 26204?>
53 <?define msierrSQLDatabaseAlreadyExists = 26205?>
54 <?define msierrPERFMONFailedRegisterDLL = 26251?>
55 <?define msierrPERFMONFailedUnregisterDLL = 26252?>
56 <?define msierrInstallPerfCounterData = 26253?>
57 <?define msierrUninstallPerfCounterData = 26254?>
58 <?define msierrSMBFailedCreate = 26301?>
59 <?define msierrSMBFailedDrop = 26302?>
60 <?define msierrCERTFailedOpen = 26351?>
61 <?define msierrCERTFailedAdd = 26352?>
62 <?define msierrUSRFailedUserCreate = 26401?>
63 <?define msierrUSRFailedUserCreatePswd = 26402?>
64 <?define msierrUSRFailedUserGroupAdd = 26403?>
65 <?define msierrUSRFailedUserCreateExists = 26404?>
66 <?define msierrUSRFailedGrantLogonAsService = 26405?>
67 <?define msierrDependencyMissingDependencies = 26451?>
68 <?define msierrDependencyHasDependents = 26452?>
69 <?define msierrDotNetRuntimeRequired = 27000?>
70 <?define msierrComPlusCannotConnect = 28001?>
71 <?define msierrComPlusPartitionReadFailed = 28002?>
72 <?define msierrComPlusPartitionRoleReadFailed = 28003?>
73 <?define msierrComPlusUserInPartitionRoleReadFailed = 28004?>
74 <?define msierrComPlusPartitionUserReadFailed = 28005?>
75 <?define msierrComPlusApplicationReadFailed = 28006?>
76 <?define msierrComPlusApplicationRoleReadFailed = 28007?>
77 <?define msierrComPlusUserInApplicationRoleReadFailed = 28008?>
78 <?define msierrComPlusAssembliesReadFailed = 28009?>
79 <?define msierrComPlusSubscriptionReadFailed = 28010?>
80 <?define msierrComPlusPartitionDependency = 28011?>
81 <?define msierrComPlusPartitionNotFound = 28012?>
82 <?define msierrComPlusPartitionIdConflict = 28013?>
83 <?define msierrComPlusPartitionNameConflict = 28014?>
84 <?define msierrComPlusApplicationDependency = 28015?>
85 <?define msierrComPlusApplicationNotFound = 28016?>
86 <?define msierrComPlusApplicationIdConflict = 28017?>
87 <?define msierrComPlusApplicationNameConflict = 28018?>
88 <?define msierrComPlusApplicationRoleDependency = 28019?>
89 <?define msierrComPlusApplicationRoleNotFound = 28020?>
90 <?define msierrComPlusApplicationRoleConflict = 28021?>
91 <?define msierrComPlusAssemblyDependency = 28022?>
92 <?define msierrComPlusSubscriptionIdConflict = 28023?>
93 <?define msierrComPlusSubscriptionNameConflict = 28024?>
94 <?define msierrComPlusFailedLookupNames = 28025?>
95 <?define msierrMsmqCannotConnect = 28101?>
96</Include> \ No newline at end of file
diff --git a/src/ext/Util/wixlib/de-de.wxl b/src/ext/Util/wixlib/de-de.wxl
new file mode 100644
index 00000000..65785a3b
--- /dev/null
+++ b/src/ext/Util/wixlib/de-de.wxl
@@ -0,0 +1,32 @@
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<WixLocalization Culture="de-de" xmlns="http://wixtoolset.org/schemas/v4/wxl">
5 <String Id="msierrUSRFailedUserCreate" Overridable="yes">Konnte den Benutzer nicht anlegen. ([2] [3] [4] [5])</String>
6 <String Id="msierrUSRFailedUserCreatePswd" Overridable="yes">Konnte den Benutzer auf Grund eines falschen Passwortes nicht anlegen. ([2] [3] [4] [5])</String>
7 <String Id="msierrUSRFailedUserGroupAdd" Overridable="yes">Konnte Benutzer nicht zur Gruppe hinzufügen. ([2] [3] [4] [5])</String>
8 <String Id="msierrUSRFailedUserCreateExists" Overridable="yes">Konnte den Benutzer nicht anlegen, da er bereits existierte. ([2] [3] [4] [5])</String>
9
10 <String Id="msierrSMBFailedCreate" Overridable="yes">Konnte Netzwerkfreigabe nicht anlegen. ([2] [3] [4] [5])</String>
11 <String Id="msierrSMBFailedDrop" Overridable="yes">Konnte Netzwerkfreigabe nicht entfernen. ([2] [3] [4] [5])</String>
12
13 <String Id="msierrPERFMONFailedRegisterDLL" Overridable="yes">Konnte die DLL nicht für PerfMon registrieren. ([2] [3] [4] [5])</String>
14 <String Id="msierrPERFMONFailedUnregisterDLL" Overridable="yes">Konnte die DLL nicht für PerfMon deregistrieren. ([2] [3] [4] [5])</String>
15
16 <String Id="msierrInstallPerfCounterData" Overridable="yes">Konnte die Daten der Leistungsüberwachung (performance counters) nicht installieren. ([2] [3] [4] [5])</String>
17 <String Id="msierrUninstallPerfCounterData" Overridable="yes">Konnte die Daten der Leistungsüberwachung (performance counters) nicht deinstallieren. ([2] [3] [4] [5])</String>
18
19 <String Id="msierrSecureObjectsFailedCreateSD" Overridable="yes">Konnte keinen Security Descriptor für [3]\[4] erstellen, System Fehler: [2]</String>
20 <String Id="msierrSecureObjectsFailedSet" Overridable="yes">Konnte keinen Security Descriptor für das Objekt [3] erstellen, System Fehler: [2]</String>
21 <String Id="msierrSecureObjectsUnknownType" Overridable="yes">Unbekannter Objekt Typ [3], System Fehler: [2]</String>
22
23 <String Id="msierrXmlFileFailedRead" Overridable="yes">Beim Lesen der XML Dateien trat ein Fehler auf.</String>
24 <String Id="msierrXmlFileFailedOpen" Overridable="yes">Konnte XML Datei [3] nicht öffnen, System Fehler: [2]</String>
25 <String Id="msierrXmlFileFailedSelect" Overridable="yes">Konnte Knoten [3] in der XML Datei [4] nicht finden, System Fehler: [2]</String>
26 <String Id="msierrXmlFileFailedSave" Overridable="yes">Beim Speichern der Änderungen an der XML Datei [3] trat ein Fehler auf, System Fehler: [2]</String>
27
28 <String Id="msierrXmlConfigFailedRead" Overridable="yes">Bei der Konfiguration der XML Dateien trat ein Fehler auf.</String>
29 <String Id="msierrXmlConfigFailedOpen" Overridable="yes">Konnte XML Datei [3] nicht öffnen, System Fehler: [2]</String>
30 <String Id="msierrXmlConfigFailedSelect" Overridable="yes">Konnte Knoten [3] in der XML Datei [4] nicht finden, System Fehler: [2]</String>
31 <String Id="msierrXmlConfigFailedSave" Overridable="yes">Beim Speichern der Änderungen an der XML Datei [3] trat ein Fehler auf, System Fehler: [2]</String>
32</WixLocalization>
diff --git a/src/ext/Util/wixlib/en-us.wxl b/src/ext/Util/wixlib/en-us.wxl
new file mode 100644
index 00000000..e8b146a4
--- /dev/null
+++ b/src/ext/Util/wixlib/en-us.wxl
@@ -0,0 +1,32 @@
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<WixLocalization Culture="en-us" xmlns="http://wixtoolset.org/schemas/v4/wxl">
5 <String Id="msierrUSRFailedUserCreate" Overridable="yes">Failed to create user. ([2] [3] [4] [5])</String>
6 <String Id="msierrUSRFailedUserCreatePswd" Overridable="yes">Failed to create user due to invalid password. ([2] [3] [4] [5])</String>
7 <String Id="msierrUSRFailedUserGroupAdd" Overridable="yes">Failed to add user to group. ([2] [3] [4] [5])</String>
8 <String Id="msierrUSRFailedUserCreateExists" Overridable="yes">Failed to create user because it already exists. ([2] [3] [4] [5])</String>
9
10 <String Id="msierrSMBFailedCreate" Overridable="yes">Failed to create network share. ([2] [3] [4] [5])</String>
11 <String Id="msierrSMBFailedDrop" Overridable="yes">Failed to drop network share. ([2] [3] [4] [5])</String>
12
13 <String Id="msierrPERFMONFailedRegisterDLL" Overridable="yes">Failed to register DLL with PerfMon. ([2] [3] [4] [5])</String>
14 <String Id="msierrPERFMONFailedUnregisterDLL" Overridable="yes">Failed to unregister DLL with PerfMon. ([2] [3] [4] [5])</String>
15
16 <String Id="msierrInstallPerfCounterData" Overridable="yes">Failed to install performance counters. ([2] [3] [4] [5])</String>
17 <String Id="msierrUninstallPerfCounterData" Overridable="yes">Failed to uninstall performance counters. ([2] [3] [4] [5])</String>
18
19 <String Id="msierrSecureObjectsFailedCreateSD" Overridable="yes">Failed to create security descriptor for [3]\[4], system error: [2]</String>
20 <String Id="msierrSecureObjectsFailedSet" Overridable="yes">Failed to set security descriptor on object [3], system error: [2]</String>
21 <String Id="msierrSecureObjectsUnknownType" Overridable="yes">Unknown Object Type [3], system error: [2]</String>
22
23 <String Id="msierrXmlFileFailedRead" Overridable="yes">There was a failure while configuring XML files.</String>
24 <String Id="msierrXmlFileFailedOpen" Overridable="yes">Failed to open XML file [3], system error: [2]</String>
25 <String Id="msierrXmlFileFailedSelect" Overridable="yes">Failed to find node: [3] in XML file: [4], system error: [2]</String>
26 <String Id="msierrXmlFileFailedSave" Overridable="yes">Failed to save changes to XML file [3], system error: [2]</String>
27
28 <String Id="msierrXmlConfigFailedRead" Overridable="yes">There was a failure while configuring XML files.</String>
29 <String Id="msierrXmlConfigFailedOpen" Overridable="yes">Failed to open XML file [3], system error: [2]</String>
30 <String Id="msierrXmlConfigFailedSelect" Overridable="yes">Failed to find node: [3] in XML file: [4], system error: [2]</String>
31 <String Id="msierrXmlConfigFailedSave" Overridable="yes">Failed to save changes to XML file [3], system error: [2]</String>
32</WixLocalization>
diff --git a/src/ext/Util/wixlib/es-es.wxl b/src/ext/Util/wixlib/es-es.wxl
new file mode 100644
index 00000000..ca5ab8bb
--- /dev/null
+++ b/src/ext/Util/wixlib/es-es.wxl
@@ -0,0 +1,31 @@
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<WixLocalization Culture="es-es" xmlns="http://wixtoolset.org/schemas/v4/wxl">
4 <String Id="msierrUSRFailedUserCreate" Overridable="yes">La creación del usuario ha fracasado. ([2] [3] [4] [5])</String>
5 <String Id="msierrUSRFailedUserCreatePswd" Overridable="yes">La creación del usuario ha fracasado porque la contraseña es incorrecta. ([2] [3] [4] [5])</String>
6 <String Id="msierrUSRFailedUserGroupAdd" Overridable="yes">El aditamento del usuario al grupo ha fracasado. ([2] [3] [4] [5])</String>
7 <String Id="msierrUSRFailedUserCreateExists" Overridable="yes">La creación del usuario ha fracasado porque ya existe. ([2] [3] [4] [5])</String>
8
9 <String Id="msierrSMBFailedCreate" Overridable="yes">La creación de la red compartida ha fracasado. ([2] [3] [4] [5])</String>
10 <String Id="msierrSMBFailedDrop" Overridable="yes">La eliminación de la red compartida ha fracasado. ([2] [3] [4] [5])</String>
11
12 <String Id="msierrPERFMONFailedRegisterDLL" Overridable="yes">La inscripción al registro de la DLL con PerfMon ha fracasado. ([2] [3] [4] [5])</String>
13 <String Id="msierrPERFMONFailedUnregisterDLL" Overridable="yes">La cancelación de la inscripción al registro de la DLL con PerfMon ha fracasado. ([2] [3] [4] [5])</String>
14
15 <String Id="msierrInstallPerfCounterData" Overridable="yes">La instalación de los contadores de rendimiento ha fracasado. ([2] [3] [4] [5])</String>
16 <String Id="msierrUninstallPerfCounterData" Overridable="yes">La desinstalación de los contadores de rendimiento ha fracasado. ([2] [3] [4] [5])</String>
17
18 <String Id="msierrSecureObjectsFailedCreateSD" Overridable="yes">La creación de los ACLs ha fracasado por [3]\[4], error del sistema : [2]</String>
19 <String Id="msierrSecureObjectsFailedSet" Overridable="yes">El posicionamiento de los ACLs por el objecto [3] ha fracasado, error del sistema: [2]</String>
20 <String Id="msierrSecureObjectsUnknownType" Overridable="yes">Tipo de objecto no conocido [3], error del sistema: [2]</String>
21
22 <String Id="msierrXmlFileFailedRead" Overridable="yes">Un problema ha aparecido durante la configuración de los ficheros XML.</String>
23 <String Id="msierrXmlFileFailedOpen" Overridable="yes">Fracaso de la apertura de los ficheros XML [3], error del sistema: [2]</String>
24 <String Id="msierrXmlFileFailedSelect" Overridable="yes">Fracaso de la búsqueda del nodo: [3] en el fichero XML: [4], error del sistema: [2]</String>
25 <String Id="msierrXmlFileFailedSave" Overridable="yes">Fracaso durante la salvaguardia de las modificaciones en el fichero XML [3], error del sistema: [2]</String>
26
27 <String Id="msierrXmlConfigFailedRead" Overridable="yes">Un problema ha aparecido durante la configuración de los ficheros XML.</String>
28 <String Id="msierrXmlConfigFailedOpen" Overridable="yes">Fracaso de la apertura de los ficheros XML [3], error del sistema: [2]</String>
29 <String Id="msierrXmlConfigFailedSelect" Overridable="yes">Fracaso de la búsqueda del nodo: [3] en el fichero XML: [4], error del sistema: [2]</String>
30 <String Id="msierrXmlConfigFailedSave" Overridable="yes">Fracaso durante la salvaguardia de las modificaciones en el fichero XML [3], error del sistema: [2]</String>
31</WixLocalization> \ No newline at end of file
diff --git a/src/ext/Util/wixlib/fr-fr.wxl b/src/ext/Util/wixlib/fr-fr.wxl
new file mode 100644
index 00000000..ad34b56a
--- /dev/null
+++ b/src/ext/Util/wixlib/fr-fr.wxl
@@ -0,0 +1,31 @@
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<WixLocalization Culture="fr-fr" xmlns="http://wixtoolset.org/schemas/v4/wxl">
4 <String Id="msierrUSRFailedUserCreate" Overridable="yes">La création de l'utilisateur a échoué. ([2] [3] [4] [5])</String>
5 <String Id="msierrUSRFailedUserCreatePswd" Overridable="yes">La création de l'utilisateur a échoué car le mot de passe est invalide. ([2] [3] [4] [5])</String>
6 <String Id="msierrUSRFailedUserGroupAdd" Overridable="yes">L'ajout de l'utilisateur au groupe a échoué. ([2] [3] [4] [5])</String>
7 <String Id="msierrUSRFailedUserCreateExists" Overridable="yes">La création de l'utilisateur a échoué car il existe dejà. ([2] [3] [4] [5])</String>
8
9 <String Id="msierrSMBFailedCreate" Overridable="yes">La création du partage reseau a échoué. ([2] [3] [4] [5])</String>
10 <String Id="msierrSMBFailedDrop" Overridable="yes">La suppression du partage reseau a échoué. ([2] [3] [4] [5])</String>
11
12 <String Id="msierrPERFMONFailedRegisterDLL" Overridable="yes">L'inscription au registre de la DLL avec PerfMon a échoué. ([2] [3] [4] [5])</String>
13 <String Id="msierrPERFMONFailedUnregisterDLL" Overridable="yes">La desinscription au registre de la DLL avec PerfMon a échoué. ([2] [3] [4] [5])</String>
14
15 <String Id="msierrInstallPerfCounterData" Overridable="yes">L'installation des compteurs de performance a échoué. ([2] [3] [4] [5])</String>
16 <String Id="msierrUninstallPerfCounterData" Overridable="yes">La desinstallation des compteurs de performance a échoué. ([2] [3] [4] [5])</String>
17
18 <String Id="msierrSecureObjectsFailedCreateSD" Overridable="yes">La création des ACLs a échoué pour [3]\[4], erreur systeme: [2]</String>
19 <String Id="msierrSecureObjectsFailedSet" Overridable="yes">Le positionnement des ACLs pour l'objet [3] a échoué, erreur systeme: [2]</String>
20 <String Id="msierrSecureObjectsUnknownType" Overridable="yes">Type d'objet inconnu [3], erreur systeme: [2]</String>
21
22 <String Id="msierrXmlFileFailedRead" Overridable="yes">Un problème est survenu lors de la configuration des fichiers XML.</String>
23 <String Id="msierrXmlFileFailedOpen" Overridable="yes">Echec de l'ouverture des fichiers XML [3], erreur systeme: [2]</String>
24 <String Id="msierrXmlFileFailedSelect" Overridable="yes">Echec de la recherche du noeud: [3] dans le fichier XML: [4], erreur systeme: [2]</String>
25 <String Id="msierrXmlFileFailedSave" Overridable="yes">Echec lors de la sauvegarde des modifications dans le fichier XML [3], erreur systeme: [2]</String>
26
27 <String Id="msierrXmlConfigFailedRead" Overridable="yes">Un problème est survenu lors de la configuration des fichiers XML.</String>
28 <String Id="msierrXmlConfigFailedOpen" Overridable="yes">Echec de l'ouverture des fichiers XML [3], erreur systeme: [2]</String>
29 <String Id="msierrXmlConfigFailedSelect" Overridable="yes">Echec de la recherche du noeud: [3] dans le fichier XML: [4], erreur systeme: [2]</String>
30 <String Id="msierrXmlConfigFailedSave" Overridable="yes">Echec lors de la sauvegarde des modifications dans le fichier XML [3], erreur systeme: [2]</String>
31</WixLocalization> \ No newline at end of file
diff --git a/src/ext/Util/wixlib/it-it.wxl b/src/ext/Util/wixlib/it-it.wxl
new file mode 100644
index 00000000..8cea0a14
--- /dev/null
+++ b/src/ext/Util/wixlib/it-it.wxl
@@ -0,0 +1,32 @@
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<WixLocalization Culture="it-it" xmlns="http://wixtoolset.org/schemas/v4/wxl">
5 <String Id="msierrUSRFailedUserCreate" Overridable="yes">Impossibile creare l'utente. ([2] [3] [4] [5])</String>
6 <String Id="msierrUSRFailedUserCreatePswd" Overridable="yes">Impossibile creare l'utente perchè la password è errata. ([2] [3] [4] [5])</String>
7 <String Id="msierrUSRFailedUserGroupAdd" Overridable="yes">Impossibile aggiungere l'utente al gruppo. ([2] [3] [4] [5])</String>
8 <String Id="msierrUSRFailedUserCreateExists" Overridable="yes">Impossibile creare l'utente perchè già esistente. ([2] [3] [4] [5])</String>
9
10 <String Id="msierrSMBFailedCreate" Overridable="yes">Impossibile creare la risorsa di rete. ([2] [3] [4] [5])</String>
11 <String Id="msierrSMBFailedDrop" Overridable="yes">Impossibile eliminare la risorsa di rete. ([2] [3] [4] [5])</String>
12
13 <String Id="msierrPERFMONFailedRegisterDLL" Overridable="yes">Impossibile registrare la DLL con PerfMon. ([2] [3] [4] [5])</String>
14 <String Id="msierrPERFMONFailedUnregisterDLL" Overridable="yes">Impossibile rimuovere la registrazione della DLL con PerfMon. ([2] [3] [4] [5])</String>
15
16 <String Id="msierrInstallPerfCounterData" Overridable="yes">Impossibile installare i contatori delle prestazioni. ([2] [3] [4] [5])</String>
17 <String Id="msierrUninstallPerfCounterData" Overridable="yes">Impossibile rimuovere i contatori delle prestazioni. ([2] [3] [4] [5])</String>
18
19 <String Id="msierrSecureObjectsFailedCreateSD" Overridable="yes">Impossibile creare i descrittori di sicurezza per [3]\[4], errore di sistema: [2]</String>
20 <String Id="msierrSecureObjectsFailedSet" Overridable="yes">Impossibile impostare i descrittori di sicurezza sull'oggetto [3], errore di sistema: [2]</String>
21 <String Id="msierrSecureObjectsUnknownType" Overridable="yes">Tipo di oggetto sconosciuto [3], errore di sistema: [2]</String>
22
23 <String Id="msierrXmlFileFailedRead" Overridable="yes">Si è verificato un errore durante la configurazione dei file XML.</String>
24 <String Id="msierrXmlFileFailedOpen" Overridable="yes">Impossibile aprire il file XML [3], errore di sistema: [2]</String>
25 <String Id="msierrXmlFileFailedSelect" Overridable="yes">Impossibile trovare il nodo: [3] nel file XML: [4], errore di sistema: [2]</String>
26 <String Id="msierrXmlFileFailedSave" Overridable="yes">Impossible salvare le modifiche al file XML [3], errore di sistema: [2]</String>
27
28 <String Id="msierrXmlConfigFailedRead" Overridable="yes">Si è verificato un errore durante la configurazione dei file XML.</String>
29 <String Id="msierrXmlConfigFailedOpen" Overridable="yes">Impossibile aprire il file XML [3], errore di sistema: [2]</String>
30 <String Id="msierrXmlConfigFailedSelect" Overridable="yes">Impossibile trovare il nodo: [3] nel file XML: [4], errore di sistema: [2]</String>
31 <String Id="msierrXmlConfigFailedSave" Overridable="yes">Impossibile salvare le modifiche al file XML [3], errore di sitema: [2]</String>
32</WixLocalization>
diff --git a/src/ext/Util/wixlib/ja-jp.wxl b/src/ext/Util/wixlib/ja-jp.wxl
new file mode 100644
index 00000000..5f5cf40d
--- /dev/null
+++ b/src/ext/Util/wixlib/ja-jp.wxl
@@ -0,0 +1,32 @@
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<WixLocalization Culture="ja-jp" xmlns="http://wixtoolset.org/schemas/v4/wxl">
5 <String Id="msierrUSRFailedUserCreate" Overridable="yes">ユーザー作成に失敗しました。 ([2] [3] [4] [5])</String>
6 <String Id="msierrUSRFailedUserCreatePswd" Overridable="yes">パスワードが無効のためユーザー作成に失敗しました。 ([2] [3] [4] [5])</String>
7 <String Id="msierrUSRFailedUserGroupAdd" Overridable="yes">ユーザーをグループに追加でいませんでした。 ([2] [3] [4] [5])</String>
8 <String Id="msierrUSRFailedUserCreateExists" Overridable="yes">ユーザーが既に存在するため作成できませんでした。 ([2] [3] [4] [5])</String>
9
10 <String Id="msierrSMBFailedCreate" Overridable="yes">ネットワーク共有の作成に失敗しました。 ([2] [3] [4] [5])</String>
11 <String Id="msierrSMBFailedDrop" Overridable="yes">ネットワーク共有の削除に失敗しました。 ([2] [3] [4] [5])</String>
12
13 <String Id="msierrPERFMONFailedRegisterDLL" Overridable="yes">DLL を PerfMon に登録でいませんでした。 ([2] [3] [4] [5])</String>
14 <String Id="msierrPERFMONFailedUnregisterDLL" Overridable="yes">DLL を PerfMon より登録解除できませんでした。 ([2] [3] [4] [5])</String>
15
16 <String Id="msierrInstallPerfCounterData" Overridable="yes">パフォーマンス カウンタをインストールできませんでした。 ([2] [3] [4] [5])</String>
17 <String Id="msierrUninstallPerfCounterData" Overridable="yes">パフォーマンス カウンタをアンインストールできませんでした。 ([2] [3] [4] [5])</String>
18
19 <String Id="msierrSecureObjectsFailedCreateSD" Overridable="yes">[3]\[4] 用セキュリティ ディスクリプターを作成できませんでした、システム エラー: [2]</String>
20 <String Id="msierrSecureObjectsFailedSet" Overridable="yes">オブジェクト [3] 上のセキュリティ ディスクリプターを設定できませんでした、システム エラー: [2]</String>
21 <String Id="msierrSecureObjectsUnknownType" Overridable="yes">不明なオブジェクト種別 [3]、システム エラー: [2]</String>
22
23 <String Id="msierrXmlFileFailedRead" Overridable="yes">XML ファイル構成中に失敗しました。</String>
24 <String Id="msierrXmlFileFailedOpen" Overridable="yes">XML ファイル [3] を開けませんでした、システム エラー: [2]</String>
25 <String Id="msierrXmlFileFailedSelect" Overridable="yes">XML ファイル [4] 内にノード [3] が見つかりませんでした、システム エラー: [2]</String>
26 <String Id="msierrXmlFileFailedSave" Overridable="yes">XML ファイル [3] へ変更を保存できませんでした、システム エラー: [2]</String>
27
28 <String Id="msierrXmlConfigFailedRead" Overridable="yes">XML ファイル構成中に失敗しました。</String>
29 <String Id="msierrXmlConfigFailedOpen" Overridable="yes">XML ファイル [3] を開けませんでした、システム エラー: [2]</String>
30 <String Id="msierrXmlConfigFailedSelect" Overridable="yes">XML ファイル [4] 内にノード [3] が見つかりませんでした、システム エラー: [2]</String>
31 <String Id="msierrXmlConfigFailedSave" Overridable="yes">XML ファイル [3] へ変更を保存できませんでした、システム エラー: [2]</String>
32</WixLocalization>
diff --git a/src/ext/Util/wixlib/pt-br.wxl b/src/ext/Util/wixlib/pt-br.wxl
new file mode 100644
index 00000000..3ca27dda
--- /dev/null
+++ b/src/ext/Util/wixlib/pt-br.wxl
@@ -0,0 +1,26 @@
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<WixLocalization Culture="pt-br" xmlns="http://wixtoolset.org/schemas/v4/wxl">
5 <String Id="msierrUSRFailedUserCreate" Overridable="yes">Falha ao criar usuário. ([2] [3] [4] [5])</String>
6 <String Id="msierrUSRFailedUserCreatePswd" Overridable="yes">Falha ao criar usuário devido a senha inválida. ([2] [3] [4] [5])</String>
7 <String Id="msierrUSRFailedUserGroupAdd" Overridable="yes">Falha ao adicionar o usuário ao grupo. ([2] [3] [4] [5])</String>
8 <String Id="msierrUSRFailedUserCreateExists" Overridable="yes">Falha ao criar o usuário, porque ele já existe. ([2] [3] [4] [5])</String>
9 <String Id="msierrSMBFailedCreate" Overridable="yes">Falha ao criar o compartilhamento de rede. ([2] [3] [4] [5])</String>
10 <String Id="msierrSMBFailedDrop" Overridable="yes">Falha ao cair compartilhamento de rede. ([2] [3] [4] [5])</String>
11 <String Id="msierrPERFMONFailedRegisterDLL" Overridable="yes">Falha ao registrar DLL com PerfMon. ([2] [3] [4] [5])</String>
12 <String Id="msierrPERFMONFailedUnregisterDLL" Overridable="yes">Falha ao cancelar o registro de DLL com PerfMon. ([2] [3] [4] [5])</String>
13 <String Id="msierrInstallPerfCounterData" Overridable="yes">Falha ao instalar contadores de desempenho. ([2] [3] [4] [5])</String>
14 <String Id="msierrUninstallPerfCounterData" Overridable="yes">Falha ao desinstalar contadores de desempenho. ([2] [3] [4] [5])</String>
15 <String Id="msierrSecureObjectsFailedCreateSD" Overridable="yes">Falha ao criar o descritor de segurança [3] \ [4], erro do sistema: [2]</String>
16 <String Id="msierrSecureObjectsFailedSet" Overridable="yes">Falha ao definir o descritor de segurança sobre o objeto [3], erro do sistema: [2]</String>
17 <String Id="msierrSecureObjectsUnknownType" Overridable="yes">Objeto Desconhecido Tipo [3], erro do sistema: [2]</String>
18 <String Id="msierrXmlFileFailedRead" Overridable="yes">Houve uma falha ao configurar arquivos XML.</String>
19 <String Id="msierrXmlFileFailedOpen" Overridable="yes">Falha ao abrir o arquivo XML [3], erro do sistema: [2]</String>
20 <String Id="msierrXmlFileFailedSelect" Overridable="yes">Falha ao localizar nó: [3] no arquivo XML: [4], erro do sistema: [2]</String>
21 <String Id="msierrXmlFileFailedSave" Overridable="yes">Falha ao salvar as alterações para o arquivo XML [3], erro do sistema: [2]</String>
22 <String Id="msierrXmlConfigFailedRead" Overridable="yes">Houve uma falha ao configurar arquivos XML.</String>
23 <String Id="msierrXmlConfigFailedOpen" Overridable="yes">Falha ao abrir o arquivo XML [3], erro do sistema: [2]</String>
24 <String Id="msierrXmlConfigFailedSelect" Overridable="yes">Falha ao localizar nó: [3] no arquivo XML: [4], erro do sistema: [2]</String>
25 <String Id="msierrXmlConfigFailedSave" Overridable="yes">Falha ao salvar as alterações para o arquivo XML [3], erro do sistema: [2]</String>
26</WixLocalization>
diff --git a/src/ext/Util/wixlib/util.v3.ncrunchproject b/src/ext/Util/wixlib/util.v3.ncrunchproject
new file mode 100644
index 00000000..319cd523
--- /dev/null
+++ b/src/ext/Util/wixlib/util.v3.ncrunchproject
@@ -0,0 +1,5 @@
1<ProjectConfiguration>
2 <Settings>
3 <IgnoreThisComponentCompletely>True</IgnoreThisComponentCompletely>
4 </Settings>
5</ProjectConfiguration> \ No newline at end of file
diff --git a/src/ext/Util/wixlib/util.wixproj b/src/ext/Util/wixlib/util.wixproj
new file mode 100644
index 00000000..99dede7d
--- /dev/null
+++ b/src/ext/Util/wixlib/util.wixproj
@@ -0,0 +1,27 @@
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<Project Sdk="WixToolset.Sdk">
3
4 <PropertyGroup>
5 <OutputType>Library</OutputType>
6 <BindFiles>true</BindFiles>
7 </PropertyGroup>
8
9 <ItemGroup>
10 <BindInputPaths Include="$(OutputPath)x86" BindName='x86' />
11 <BindInputPaths Include="$(OutputPath)x64" BindName='x64' />
12 <BindInputPaths Include="$(OutputPath)arm64" BindName='arm64' />
13 </ItemGroup>
14
15 <ItemGroup>
16 <ProjectReference Include="..\be\utilbe.vcxproj" Properties="Platform=ARM64" ReferenceOutputAssembly="false" />
17 <ProjectReference Include="..\be\utilbe.vcxproj" Properties="Platform=x86" ReferenceOutputAssembly="false" />
18 <ProjectReference Include="..\be\utilbe.vcxproj" Properties="Platform=x64" ReferenceOutputAssembly="false" />
19 <ProjectReference Include="..\ca\utilca.vcxproj" Properties="Platform=ARM64" ReferenceOutputAssembly="false" />
20 <ProjectReference Include="..\ca\utilca.vcxproj" Properties="Platform=x86" ReferenceOutputAssembly="false" />
21 <ProjectReference Include="..\ca\utilca.vcxproj" Properties="Platform=x64" ReferenceOutputAssembly="false" />
22 </ItemGroup>
23
24 <ItemGroup>
25 <PackageReference Include="Nerdbank.GitVersioning" Version="3.3.37" PrivateAssets="All" />
26 </ItemGroup>
27</Project>