aboutsummaryrefslogtreecommitdiff
path: root/src/libs/wcautil
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2021-04-22 15:43:19 -0700
committerRob Mensching <rob@firegiant.com>2021-04-29 16:22:14 -0700
commit30827588eb0c189b7e2d04693d116080d333200e (patch)
tree866a7987d175ccac7f506dac6ba52ca16b5f9f08 /src/libs/wcautil
parent46897fefd6aef4168f35c1f366c833d715c3bdaa (diff)
downloadwix-30827588eb0c189b7e2d04693d116080d333200e.tar.gz
wix-30827588eb0c189b7e2d04693d116080d333200e.tar.bz2
wix-30827588eb0c189b7e2d04693d116080d333200e.zip
Move wcautil into libs/wcautil
Diffstat (limited to 'src/libs/wcautil')
-rw-r--r--src/libs/wcautil/Cpp.Build.props104
-rw-r--r--src/libs/wcautil/Directory.Build.props26
-rw-r--r--src/libs/wcautil/Directory.Build.targets48
-rw-r--r--src/libs/wcautil/NativeMultiTargeting.Build.props10
-rw-r--r--src/libs/wcautil/README.md2
-rw-r--r--src/libs/wcautil/WixToolset.WcaUtil/build/WixToolset.WcaUtil.props28
-rw-r--r--src/libs/wcautil/WixToolset.WcaUtil/custommsierrors.h130
-rw-r--r--src/libs/wcautil/WixToolset.WcaUtil/exbinary.cpp142
-rw-r--r--src/libs/wcautil/WixToolset.WcaUtil/inc/wcalog.h14
-rw-r--r--src/libs/wcautil/WixToolset.WcaUtil/inc/wcautil.h397
-rw-r--r--src/libs/wcautil/WixToolset.WcaUtil/inc/wcawow64.h20
-rw-r--r--src/libs/wcautil/WixToolset.WcaUtil/inc/wcawrapquery.h130
-rw-r--r--src/libs/wcautil/WixToolset.WcaUtil/packages.config5
-rw-r--r--src/libs/wcautil/WixToolset.WcaUtil/precomp.h19
-rw-r--r--src/libs/wcautil/WixToolset.WcaUtil/qtexec.cpp340
-rw-r--r--src/libs/wcautil/WixToolset.WcaUtil/wcalog.cpp269
-rw-r--r--src/libs/wcautil/WixToolset.WcaUtil/wcascript.cpp429
-rw-r--r--src/libs/wcautil/WixToolset.WcaUtil/wcautil.cpp243
-rw-r--r--src/libs/wcautil/WixToolset.WcaUtil/wcautil.nuspec30
-rw-r--r--src/libs/wcautil/WixToolset.WcaUtil/wcautil.vcxproj94
-rw-r--r--src/libs/wcautil/WixToolset.WcaUtil/wcawow64.cpp169
-rw-r--r--src/libs/wcautil/WixToolset.WcaUtil/wcawrap.cpp1668
-rw-r--r--src/libs/wcautil/WixToolset.WcaUtil/wcawrapquery.cpp717
-rw-r--r--src/libs/wcautil/appveyor.cmd20
-rw-r--r--src/libs/wcautil/appveyor.yml35
-rw-r--r--src/libs/wcautil/nuget.config8
-rw-r--r--src/libs/wcautil/wcautil.sln37
27 files changed, 5134 insertions, 0 deletions
diff --git a/src/libs/wcautil/Cpp.Build.props b/src/libs/wcautil/Cpp.Build.props
new file mode 100644
index 00000000..44a042c7
--- /dev/null
+++ b/src/libs/wcautil/Cpp.Build.props
@@ -0,0 +1,104 @@
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 </PropertyGroup>
10
11 <PropertyGroup Condition="'$(WindowsTargetPlatformVersion)'=='' AND '$(VisualStudioVersion)'>='15.0'">
12 <WindowsTargetPlatformVersion>$([Microsoft.Build.Utilities.ToolLocationHelper]::GetLatestSDKTargetPlatformVersion('Windows', '10.0'))</WindowsTargetPlatformVersion>
13 </PropertyGroup>
14
15 <ItemDefinitionGroup>
16 <ClCompile>
17 <DisableSpecificWarnings>$(DisableSpecificCompilerWarnings)</DisableSpecificWarnings>
18 <WarningLevel>Level4</WarningLevel>
19 <AdditionalIncludeDirectories>$(ProjectDir)inc;$(MSBuildProjectDirectory);$(IntDir);$(SqlCESdkIncludePath);$(ProjectAdditionalIncludeDirectories);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
20 <PreprocessorDefinitions>WIN32;_WINDOWS;_WIN32_MSI=500;_WIN32_WINNT=0x0501;$(ArmPreprocessorDefinitions);$(UnicodePreprocessorDefinitions);_CRT_STDIO_LEGACY_WIDE_SPECIFIERS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
21 <PrecompiledHeader>Use</PrecompiledHeader>
22 <PrecompiledHeaderFile>precomp.h</PrecompiledHeaderFile>
23 <CallingConvention Condition="'$(Platform)'=='Win32'">StdCall</CallingConvention>
24 <TreatWarningAsError>true</TreatWarningAsError>
25 <ExceptionHandling>false</ExceptionHandling>
26 <AdditionalOptions>-YlprecompDefine</AdditionalOptions>
27 <AdditionalOptions Condition=" $(PlatformToolset.StartsWith('v14')) ">/Zc:threadSafeInit- %(AdditionalOptions)</AdditionalOptions>
28 <MultiProcessorCompilation Condition=" $(NUMBER_OF_PROCESSORS) &gt; 4 ">true</MultiProcessorCompilation>
29 </ClCompile>
30 <ResourceCompile>
31 <PreprocessorDefinitions>$(ArmPreprocessorDefinitions);%(PreprocessorDefinitions)</PreprocessorDefinitions>
32 <AdditionalIncludeDirectories>$(ProjectAdditionalResourceIncludeDirectories);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
33 </ResourceCompile>
34 <Lib>
35 <AdditionalLibraryDirectories>$(OutDir);$(AdditionalMultiTargetLibraryPath);$(ProjectAdditionalLibraryDirectories);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
36 </Lib>
37 <Link>
38 <SubSystem>$(ProjectSubSystem)</SubSystem>
39 <ModuleDefinitionFile>$(ProjectModuleDefinitionFile)</ModuleDefinitionFile>
40 <NoEntryPoint>$(ResourceOnlyDll)</NoEntryPoint>
41 <GenerateDebugInformation>true</GenerateDebugInformation>
42 <AdditionalDependencies>$(ProjectAdditionalLinkLibraries);advapi32.lib;comdlg32.lib;user32.lib;oleaut32.lib;gdi32.lib;shell32.lib;ole32.lib;version.lib;%(AdditionalDependencies)</AdditionalDependencies>
43 <AdditionalLibraryDirectories>$(OutDir);$(AdditionalMultiTargetLibraryPath);$(ArmLibraryDirectories);$(ProjectAdditionalLinkLibraryDirectories);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
44 <AdditionalOptions Condition=" $(PlatformToolset.StartsWith('v14')) ">/IGNORE:4099 %(AdditionalOptions)</AdditionalOptions>
45 </Link>
46 </ItemDefinitionGroup>
47
48 <ItemDefinitionGroup Condition=" '$(Platform)'=='Win32' and '$(PlatformToolset)'!='v100'">
49 <ClCompile>
50 <EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
51 </ClCompile>
52 </ItemDefinitionGroup>
53 <ItemDefinitionGroup Condition=" '$(Platform)'=='arm' ">
54 <ClCompile>
55 <CallingConvention>CDecl</CallingConvention>
56 </ClCompile>
57 </ItemDefinitionGroup>
58 <ItemDefinitionGroup Condition=" '$(ConfigurationType)'=='StaticLibrary' ">
59 <ClCompile>
60 <DebugInformationFormat>OldStyle</DebugInformationFormat>
61 <OmitDefaultLibName>true</OmitDefaultLibName>
62 <IgnoreAllDefaultLibraries>true</IgnoreAllDefaultLibraries>
63 </ClCompile>
64 </ItemDefinitionGroup>
65 <ItemDefinitionGroup Condition=" '$(Configuration)'=='Debug' ">
66 <ClCompile>
67 <Optimization>Disabled</Optimization>
68 <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
69 <PreprocessorDefinitions>_DEBUG;DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
70 <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
71 </ClCompile>
72 </ItemDefinitionGroup>
73 <ItemDefinitionGroup Condition=" '$(Configuration)'=='Debug' and '$(CLRSupport)'=='true' ">
74 <ClCompile>
75 <BasicRuntimeChecks></BasicRuntimeChecks>
76 <RuntimeLibrary>MultiThreadedDebugDll</RuntimeLibrary>
77 </ClCompile>
78 </ItemDefinitionGroup>
79 <ItemDefinitionGroup Condition=" '$(Configuration)'=='Release' ">
80 <ClCompile>
81 <Optimization>MinSpace</Optimization>
82 <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
83 <FunctionLevelLinking>true</FunctionLevelLinking>
84 <IntrinsicFunctions>true</IntrinsicFunctions>
85 <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
86 </ClCompile>
87 <Link>
88 <EnableCOMDATFolding>true</EnableCOMDATFolding>
89 <OptimizeReferences>true</OptimizeReferences>
90 </Link>
91 </ItemDefinitionGroup>
92 <ItemDefinitionGroup Condition=" '$(Configuration)'=='Release' and '$(CLRSupport)'=='true' ">
93 <ClCompile>
94 <BasicRuntimeChecks></BasicRuntimeChecks>
95 <RuntimeLibrary>MultiThreadedDll</RuntimeLibrary>
96 </ClCompile>
97 </ItemDefinitionGroup>
98 <ItemDefinitionGroup Condition=" '$(CLRSupport)'=='true' ">
99 <Link>
100 <KeyFile>$(LinkKeyFile)</KeyFile>
101 <DelaySign>$(LinkDelaySign)</DelaySign>
102 </Link>
103 </ItemDefinitionGroup>
104</Project>
diff --git a/src/libs/wcautil/Directory.Build.props b/src/libs/wcautil/Directory.Build.props
new file mode 100644
index 00000000..e853e22d
--- /dev/null
+++ b/src/libs/wcautil/Directory.Build.props
@@ -0,0 +1,26 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3<!--
4 Do NOT modify this file. Update the canonical version in Home\repo-template\src\Directory.Build.props
5 then update all of the repos.
6-->
7<Project>
8 <PropertyGroup>
9 <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
10 <EnableSourceLink Condition=" '$(NCrunch)' == '1' ">false</EnableSourceLink>
11
12 <ProjectName Condition=" '$(ProjectName)' == '' ">$(MSBuildProjectName)</ProjectName>
13 <BaseOutputPath>$([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)..\build\))</BaseOutputPath>
14 <BaseIntermediateOutputPath>$(BaseOutputPath)obj\$(ProjectName)\</BaseIntermediateOutputPath>
15 <OutputPath>$(BaseOutputPath)$(Configuration)\</OutputPath>
16
17 <Authors>WiX Toolset Team</Authors>
18 <Company>WiX Toolset</Company>
19 <Copyright>Copyright (c) .NET Foundation and contributors. All rights reserved.</Copyright>
20 <PackageLicenseExpression>MS-RL</PackageLicenseExpression>
21 <Product>WiX Toolset</Product>
22 </PropertyGroup>
23
24 <Import Project="Cpp.Build.props" Condition=" '$(MSBuildProjectExtension)'=='.vcxproj' " />
25 <Import Project="Custom.Build.props" Condition=" Exists('Custom.Build.props') " />
26</Project>
diff --git a/src/libs/wcautil/Directory.Build.targets b/src/libs/wcautil/Directory.Build.targets
new file mode 100644
index 00000000..dac7452a
--- /dev/null
+++ b/src/libs/wcautil/Directory.Build.targets
@@ -0,0 +1,48 @@
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</Project>
diff --git a/src/libs/wcautil/NativeMultiTargeting.Build.props b/src/libs/wcautil/NativeMultiTargeting.Build.props
new file mode 100644
index 00000000..1ff46559
--- /dev/null
+++ b/src/libs/wcautil/NativeMultiTargeting.Build.props
@@ -0,0 +1,10 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3
4<Project>
5 <!-- Overrides the standard Cpp.Build.props to include the PlatformToolset in the output path. -->
6 <PropertyGroup>
7 <IntDir>$(BaseIntermediateOutputPath)$(Configuration)\$(PlatformToolset)\$(PlatformTarget)\</IntDir>
8 <OutDir>$(OutputPath)$(PlatformToolset)\$(PlatformTarget)\</OutDir>
9 </PropertyGroup>
10</Project>
diff --git a/src/libs/wcautil/README.md b/src/libs/wcautil/README.md
new file mode 100644
index 00000000..aa004554
--- /dev/null
+++ b/src/libs/wcautil/README.md
@@ -0,0 +1,2 @@
1# wcautil
2wcautil.lib - WiX Toolset Custom Action native utility library
diff --git a/src/libs/wcautil/WixToolset.WcaUtil/build/WixToolset.WcaUtil.props b/src/libs/wcautil/WixToolset.WcaUtil/build/WixToolset.WcaUtil.props
new file mode 100644
index 00000000..fb73a680
--- /dev/null
+++ b/src/libs/wcautil/WixToolset.WcaUtil/build/WixToolset.WcaUtil.props
@@ -0,0 +1,28 @@
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 ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
5 <ItemDefinitionGroup>
6 <ClCompile>
7 <AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)native\include\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
8 </ClCompile>
9 <ResourceCompile>
10 <AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)native\include\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
11 </ResourceCompile>
12 </ItemDefinitionGroup>
13 <ItemDefinitionGroup Condition=" $(PlatformToolset.ToLower().StartsWith('v140')) ">
14 <Link>
15 <AdditionalDependencies>$(MSBuildThisFileDirectory)native\v140\$(PlatformTarget)\wcautil.lib;%(AdditionalDependencies)</AdditionalDependencies>
16 </Link>
17 </ItemDefinitionGroup>
18 <ItemDefinitionGroup Condition=" $(PlatformToolset.ToLower().StartsWith('v141')) ">
19 <Link>
20 <AdditionalDependencies>$(MSBuildThisFileDirectory)native\v141\$(PlatformTarget)\wcautil.lib;%(AdditionalDependencies)</AdditionalDependencies>
21 </Link>
22 </ItemDefinitionGroup>
23 <ItemDefinitionGroup Condition=" $(PlatformToolset.ToLower().StartsWith('v142')) ">
24 <Link>
25 <AdditionalDependencies>$(MSBuildThisFileDirectory)native\v142\$(PlatformTarget)\wcautil.lib;%(AdditionalDependencies)</AdditionalDependencies>
26 </Link>
27 </ItemDefinitionGroup>
28</Project>
diff --git a/src/libs/wcautil/WixToolset.WcaUtil/custommsierrors.h b/src/libs/wcautil/WixToolset.WcaUtil/custommsierrors.h
new file mode 100644
index 00000000..f149fb31
--- /dev/null
+++ b/src/libs/wcautil/WixToolset.WcaUtil/custommsierrors.h
@@ -0,0 +1,130 @@
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 GLOBAL_ERROR_BASE 25501
6
7#define msierrSecureObjectsFailedCreateSD 25520
8#define msierrSecureObjectsFailedSet 25521
9#define msierrSecureObjectsUnknownType 25522
10
11#define msierrXmlFileFailedRead 25530
12#define msierrXmlFileFailedOpen 25531
13#define msierrXmlFileFailedSelect 25532
14#define msierrXmlFileFailedSave 25533
15
16#define msierrXmlConfigFailedRead 25540
17#define msierrXmlConfigFailedOpen 25541
18#define msierrXmlConfigFailedSelect 25542
19#define msierrXmlConfigFailedSave 25543
20
21#define msierrFirewallCannotConnect 25580
22
23//---------------------------------------------------------------------------
24// Server CustomAction Errors
25// SERVER range: 26001-26100
26#define SERVER_ERROR_BASE 26000
27
28#define msierrIISCannotConnect 26001
29#define msierrIISFailedReadWebSite 26002
30#define msierrIISFailedReadWebDirs 26003
31#define msierrIISFailedReadVDirs 26004
32#define msierrIISFailedReadFilters 26005
33#define msierrIISFailedReadAppPool 26006
34#define msierrIISFailedReadMimeMap 26007
35#define msierrIISFailedReadProp 26008
36#define msierrIISFailedReadWebSvcExt 26009
37#define msierrIISFailedReadWebError 26010
38#define msierrIISFailedReadHttpHeader 26011
39
40#define msierrIISFailedSchedTransaction 26031
41#define msierrIISFailedSchedInstallWebs 26032
42#define msierrIISFailedSchedInstallWebDirs 26033
43#define msierrIISFailedSchedInstallVDirs 26034
44#define msierrIISFailedSchedInstallFilters 26035
45#define msierrIISFailedSchedInstallAppPool 26036
46#define msierrIISFailedSchedInstallProp 26037
47#define msierrIISFailedSchedInstallWebSvcExt 26038
48
49#define msierrIISFailedSchedUninstallWebs 26051
50#define msierrIISFailedSchedUninstallWebDirs 26052
51#define msierrIISFailedSchedUninstallVDirs 26053
52#define msierrIISFailedSchedUninstallFilters 26054
53#define msierrIISFailedSchedUninstallAppPool 26055
54#define msierrIISFailedSchedUninstallProp 26056
55#define msierrIISFailedSchedUninstallWebSvcExt 26057
56
57#define msierrIISFailedStartTransaction 26101
58#define msierrIISFailedOpenKey 26102
59#define msierrIISFailedCreateKey 26103
60#define msierrIISFailedWriteData 26104
61#define msierrIISFailedCreateApp 26105
62#define msierrIISFailedDeleteKey 26106
63#define msierrIISFailedDeleteApp 26107
64#define msierrIISFailedDeleteValue 26108
65#define msierrIISFailedCommitInUse 26109
66
67#define msierrSQLFailedCreateDatabase 26201
68#define msierrSQLFailedDropDatabase 26202
69#define msierrSQLFailedConnectDatabase 26203
70#define msierrSQLFailedExecString 26204
71#define msierrSQLDatabaseAlreadyExists 26205
72
73#define msierrPERFMONFailedRegisterDLL 26251
74#define msierrPERFMONFailedUnregisterDLL 26252
75#define msierrInstallPerfCounterData 26253
76#define msierrUninstallPerfCounterData 26254
77
78#define msierrSMBFailedCreate 26301
79#define msierrSMBFailedDrop 26302
80
81#define msierrCERTFailedOpen 26351
82#define msierrCERTFailedAdd 26352
83
84#define msierrUSRFailedUserCreate 26401
85#define msierrUSRFailedUserCreatePswd 26402
86#define msierrUSRFailedUserGroupAdd 26403
87#define msierrUSRFailedUserCreateExists 26404
88#define msierrUSRFailedGrantLogonAsService 26405
89
90#define msierrDependencyMissingDependencies 26451
91#define msierrDependencyHasDependents 26452
92
93//--------------------------------------------------------------------------
94// Managed code CustomAction Errors
95// MANAGED range: 27000-27100
96#define MANAGED_ERROR_BASE 27000
97
98#define msierrDotNetRuntimeRequired 27000
99//---------------------------------------------------------------------------
100// Public CustomAction Errors
101// PUBLIC range: 28001-28100
102#define PUBLIC_ERROR_BASE 28000
103
104#define msierrComPlusCannotConnect 28001
105#define msierrComPlusPartitionReadFailed 28002
106#define msierrComPlusPartitionRoleReadFailed 28003
107#define msierrComPlusUserInPartitionRoleReadFailed 28004
108#define msierrComPlusPartitionUserReadFailed 28005
109#define msierrComPlusApplicationReadFailed 28006
110#define msierrComPlusApplicationRoleReadFailed 28007
111#define msierrComPlusUserInApplicationRoleReadFailed 28008
112#define msierrComPlusAssembliesReadFailed 28009
113#define msierrComPlusSubscriptionReadFailed 28010
114#define msierrComPlusPartitionDependency 28011
115#define msierrComPlusPartitionNotFound 28012
116#define msierrComPlusPartitionIdConflict 28013
117#define msierrComPlusPartitionNameConflict 28014
118#define msierrComPlusApplicationDependency 28015
119#define msierrComPlusApplicationNotFound 28016
120#define msierrComPlusApplicationIdConflict 28017
121#define msierrComPlusApplicationNameConflict 28018
122#define msierrComPlusApplicationRoleDependency 28019
123#define msierrComPlusApplicationRoleNotFound 28020
124#define msierrComPlusApplicationRoleConflict 28021
125#define msierrComPlusAssemblyDependency 28022
126#define msierrComPlusSubscriptionIdConflict 28023
127#define msierrComPlusSubscriptionNameConflict 28024
128#define msierrComPlusFailedLookupNames 28025
129
130#define msierrMsmqCannotConnect 28101
diff --git a/src/libs/wcautil/WixToolset.WcaUtil/exbinary.cpp b/src/libs/wcautil/WixToolset.WcaUtil/exbinary.cpp
new file mode 100644
index 00000000..5ff24212
--- /dev/null
+++ b/src/libs/wcautil/WixToolset.WcaUtil/exbinary.cpp
@@ -0,0 +1,142 @@
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// Extracts the data from the Binary table row with the given ID into a buffer.
7//
8HRESULT WIXAPI WcaExtractBinaryToBuffer(
9 __in LPCWSTR wzBinaryId,
10 __out BYTE** pbData,
11 __out DWORD* pcbData
12 )
13{
14 HRESULT hr = S_OK;
15 LPWSTR pwzSql = NULL;
16 PMSIHANDLE hView;
17 PMSIHANDLE hRec;
18
19 // make sure we're not horked from the get-go
20 hr = WcaTableExists(L"Binary");
21 if (S_OK != hr)
22 {
23 if (SUCCEEDED(hr))
24 {
25 hr = E_UNEXPECTED;
26 }
27 ExitOnFailure(hr, "There is no Binary table.");
28 }
29
30 ExitOnNull(wzBinaryId, hr, E_INVALIDARG, "Binary ID cannot be null");
31 ExitOnNull(*wzBinaryId, hr, E_INVALIDARG, "Binary ID cannot be empty string");
32
33 hr = StrAllocFormatted(&pwzSql, L"SELECT `Data` FROM `Binary` WHERE `Name`=\'%ls\'", wzBinaryId);
34 ExitOnFailure(hr, "Failed to allocate Binary table query.");
35
36 hr = WcaOpenExecuteView(pwzSql, &hView);
37 ExitOnFailure(hr, "Failed to open view on Binary table");
38
39 hr = WcaFetchSingleRecord(hView, &hRec);
40 ExitOnFailure(hr, "Failed to retrieve request from Binary table");
41
42 hr = WcaGetRecordStream(hRec, 1, pbData, pcbData);
43 ExitOnFailure(hr, "Failed to read Binary.Data.");
44
45LExit:
46 ReleaseStr(pwzSql);
47
48 return hr;
49}
50
51//
52// Extracts the data from the Binary table row with the given ID into a file.
53//
54HRESULT WIXAPI WcaExtractBinaryToFile(
55 __in LPCWSTR wzBinaryId,
56 __in LPCWSTR wzPath
57 )
58{
59 HRESULT hr = S_OK;
60 BYTE* pbData = NULL;
61 DWORD cbData = 0;
62 HANDLE hFile = INVALID_HANDLE_VALUE;
63
64 // grab the bits
65 hr = WcaExtractBinaryToBuffer(wzBinaryId, &pbData, &cbData);
66 ExitOnFailure(hr, "Failed to extract binary data: %ls", wzBinaryId);
67
68 // write 'em to the file
69 hFile = ::CreateFileW(wzPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
70 if (INVALID_HANDLE_VALUE == hFile)
71 {
72 ExitWithLastError(hr, "Failed to create file: %ls", wzPath);
73 }
74
75 DWORD cbWritten = 0;
76 if (!::WriteFile(hFile, pbData, cbData, &cbWritten, NULL))
77 {
78 ExitWithLastError(hr, "Failed to write data to file: %ls", wzPath);
79 }
80
81LExit:
82 ReleaseFile(hFile);
83 ReleaseMem(pbData);
84
85 return hr;
86}
87
88//
89// Extracts the data from the Binary table row with the given ID into a string.
90//
91HRESULT WIXAPI WcaExtractBinaryToString(
92 __in LPCWSTR wzBinaryId,
93 __deref_out_z LPWSTR* psczOutput,
94 __out WCA_ENCODING* encoding
95 )
96{
97 HRESULT hr = S_OK;
98 BYTE* pbData = NULL;
99 DWORD cbData = 0;
100
101 // grab the bits
102 hr = WcaExtractBinaryToBuffer(wzBinaryId, &pbData, &cbData);
103 ExitOnFailure(hr, "Failed to extract binary data: %ls", wzBinaryId);
104
105 // expand by a NULL character (or two) to make sure the buffer is null-terminated
106 cbData += 2;
107 pbData = reinterpret_cast<LPBYTE>(MemReAlloc(pbData, cbData, TRUE));
108 ExitOnNull(pbData, hr, E_OUTOFMEMORY, "Failed to expand binary buffer");
109
110 // Check for BOMs.
111 if (2 < cbData)
112 {
113 if ((0xFF == *pbData) && (0xFE == *(pbData + 1)))
114 {
115 *encoding = WCA_ENCODING_UTF_16;
116 hr = StrAllocString(psczOutput, reinterpret_cast<LPWSTR>(pbData), 0);
117 }
118 else if ((0xEF == *pbData) && (0xBB == *(pbData + 1)) && (0xBF == *(pbData + 2)))
119 {
120 *encoding = WCA_ENCODING_UTF_8;
121 hr = StrAllocStringAnsi(psczOutput, reinterpret_cast<LPCSTR>(pbData), 0, CP_UTF8);
122 }
123 else
124 {
125 *encoding = WCA_ENCODING_ANSI;
126 hr = StrAllocStringAnsi(psczOutput, reinterpret_cast<LPCSTR>(pbData), 0, CP_ACP);
127 }
128 ExitOnFailure(hr, "Failed to allocate string for binary buffer.");
129 }
130
131 // Free the byte buffer since it has been converted to a new UNICODE string, one way or another.
132 if (pbData)
133 {
134 WcaFreeStream(pbData);
135 pbData = NULL;
136 }
137
138LExit:
139 ReleaseMem(pbData);
140
141 return hr;
142}
diff --git a/src/libs/wcautil/WixToolset.WcaUtil/inc/wcalog.h b/src/libs/wcautil/WixToolset.WcaUtil/inc/wcalog.h
new file mode 100644
index 00000000..ffa3fa03
--- /dev/null
+++ b/src/libs/wcautil/WixToolset.WcaUtil/inc/wcalog.h
@@ -0,0 +1,14 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9BOOL WIXAPI IsVerboseLogging();
10HRESULT WIXAPI SetVerboseLoggingAtom(BOOL bValue);
11
12#ifdef __cplusplus
13}
14#endif
diff --git a/src/libs/wcautil/WixToolset.WcaUtil/inc/wcautil.h b/src/libs/wcautil/WixToolset.WcaUtil/inc/wcautil.h
new file mode 100644
index 00000000..4d036a9d
--- /dev/null
+++ b/src/libs/wcautil/WixToolset.WcaUtil/inc/wcautil.h
@@ -0,0 +1,397 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9#include "dutilsources.h"
10
11#define WIXAPI __stdcall
12#ifndef DUTIL_SOURCE_DEFAULT
13#define DUTIL_SOURCE_DEFAULT DUTIL_SOURCE_EXTERNAL
14#endif
15
16#include "dutil.h"
17
18#define MessageExitOnLastErrorSource(d, x, e, s, ...) { x = ::GetLastError(); x = HRESULT_FROM_WIN32(x); if (FAILED(x)) { ExitTraceSource(d, x, "%s", s, __VA_ARGS__); WcaErrorMessage(e, x, MB_OK, -1, __VA_ARGS__); goto LExit; } }
19#define MessageExitOnFailureSource(d, x, e, s, ...) if (FAILED(x)) { ExitTraceSource(d, x, "%s", s, __VA_ARGS__); WcaErrorMessage(e, x, INSTALLMESSAGE_ERROR | MB_OK, -1, __VA_ARGS__); goto LExit; }
20#define MessageExitOnNullWithLastErrorSource(d, p, x, e, s, ...) if (NULL == p) { x = ::GetLastError(); x = HRESULT_FROM_WIN32(x); if (!FAILED(x)) { x = E_FAIL; } ExitTraceSource(d, x, "%s", s, __VA_ARGS__); WcaErrorMessage(e, x, MB_OK, -1, __VA_ARGS__); goto LExit; }
21
22#define MessageExitOnLastError(x, e, s, ...) MessageExitOnLastErrorSource(DUTIL_SOURCE_DEFAULT, x, e, s, __VA_ARGS__)
23#define MessageExitOnFailure(x, e, s, ...) MessageExitOnFailureSource(DUTIL_SOURCE_DEFAULT, x, e, s, __VA_ARGS__)
24#define MessageExitOnNullWithLastError(p, x, e, s, ...) MessageExitOnNullWithLastErrorSource(DUTIL_SOURCE_DEFAULT, p, x, e, s, __VA_ARGS__)
25
26// Generic action enum.
27typedef enum WCA_ACTION
28{
29 WCA_ACTION_NONE,
30 WCA_ACTION_INSTALL,
31 WCA_ACTION_UNINSTALL,
32} WCA_ACTION;
33
34typedef enum WCA_CASCRIPT
35{
36 WCA_CASCRIPT_SCHEDULED,
37 WCA_CASCRIPT_ROLLBACK,
38} WCA_CASCRIPT;
39
40typedef enum WCA_CASCRIPT_CLOSE
41{
42 WCA_CASCRIPT_CLOSE_PRESERVE,
43 WCA_CASCRIPT_CLOSE_DELETE,
44} WCA_CASCRIPT_CLOSE;
45
46typedef enum WCA_TODO
47{
48 WCA_TODO_UNKNOWN,
49 WCA_TODO_INSTALL,
50 WCA_TODO_UNINSTALL,
51 WCA_TODO_REINSTALL,
52} WCA_TODO;
53
54typedef struct WCA_CASCRIPT_STRUCT
55{
56 LPWSTR pwzScriptPath;
57 HANDLE hScriptFile;
58} *WCA_CASCRIPT_HANDLE;
59
60typedef enum WCA_ENCODING
61{
62 WCA_ENCODING_UNKNOWN,
63 WCA_ENCODING_UTF_16,
64 WCA_ENCODING_UTF_8,
65 WCA_ENCODING_ANSI,
66} WCA_ENCODING;
67
68void WIXAPI WcaGlobalInitialize(
69 __in HINSTANCE hInst
70 );
71void WIXAPI WcaGlobalFinalize();
72
73HRESULT WIXAPI WcaInitialize(
74 __in MSIHANDLE hInstall,
75 __in_z PCSTR szCustomActionLogName
76 );
77UINT WIXAPI WcaFinalize(
78 __in UINT iReturnValue
79 );
80BOOL WIXAPI WcaIsInitialized();
81
82MSIHANDLE WIXAPI WcaGetInstallHandle();
83MSIHANDLE WIXAPI WcaGetDatabaseHandle();
84
85const char* WIXAPI WcaGetLogName();
86
87void WIXAPI WcaSetReturnValue(
88 __in UINT iReturnValue
89 );
90BOOL WIXAPI WcaCancelDetected();
91
92#define LOG_BUFFER 2048
93typedef enum LOGLEVEL
94{
95 LOGMSG_TRACEONLY, // Never written to the log file (except in DEBUG builds)
96 LOGMSG_VERBOSE, // Written to log when LOGVERBOSE
97 LOGMSG_STANDARD // Written to log whenever informational logging is enabled
98} LOGLEVEL;
99
100void __cdecl WcaLog(
101 __in LOGLEVEL llv,
102 __in_z __format_string PCSTR fmt, ...
103 );
104BOOL WIXAPI WcaDisplayAssert(
105 __in LPCSTR sz
106 );
107void __cdecl WcaLogError(
108 __in HRESULT hr,
109 __in LPCSTR szMessage,
110 ...
111 );
112void WIXAPI WcaLogErrorArgs(
113 __in HRESULT hr,
114 __in LPCSTR szMessage,
115 __in va_list args
116 );
117
118UINT WIXAPI WcaProcessMessage(
119 __in INSTALLMESSAGE eMessageType,
120 __in MSIHANDLE hRecord
121 );
122UINT __cdecl WcaErrorMessage(
123 __in int iError,
124 __in HRESULT hrError,
125 __in UINT uiType,
126 __in INT cArgs,
127 ...
128 );
129HRESULT WIXAPI WcaProgressMessage(
130 __in UINT uiCost,
131 __in BOOL fExtendProgressBar
132 );
133
134BOOL WIXAPI WcaIsInstalling(
135 __in INSTALLSTATE isInstalled,
136 __in INSTALLSTATE isAction
137 );
138BOOL WIXAPI WcaIsReInstalling(
139 __in INSTALLSTATE isInstalled,
140 __in INSTALLSTATE isAction
141 );
142BOOL WIXAPI WcaIsUninstalling(
143 __in INSTALLSTATE isInstalled,
144 __in INSTALLSTATE isAction
145 );
146
147HRESULT WIXAPI WcaSetComponentState(
148 __in_z LPCWSTR wzComponent,
149 __in INSTALLSTATE isState
150 );
151
152HRESULT WIXAPI WcaTableExists(
153 __in_z LPCWSTR wzTable
154 );
155
156HRESULT WIXAPI WcaOpenView(
157 __in_z LPCWSTR wzSql,
158 __out MSIHANDLE* phView
159 );
160HRESULT WIXAPI WcaExecuteView(
161 __in MSIHANDLE hView,
162 __in MSIHANDLE hRec
163 );
164HRESULT WIXAPI WcaOpenExecuteView(
165 __in_z LPCWSTR wzSql,
166 __out MSIHANDLE* phView
167 );
168HRESULT WIXAPI WcaFetchRecord(
169 __in MSIHANDLE hView,
170 __out MSIHANDLE* phRec
171 );
172HRESULT WIXAPI WcaFetchSingleRecord(
173 __in MSIHANDLE hView,
174 __out MSIHANDLE* phRec
175 );
176
177HRESULT WIXAPI WcaGetProperty(
178 __in_z LPCWSTR wzProperty,
179 __inout LPWSTR* ppwzData
180 );
181HRESULT WIXAPI WcaGetFormattedProperty(
182 __in_z LPCWSTR wzProperty,
183 __out LPWSTR* ppwzData
184 );
185HRESULT WIXAPI WcaGetFormattedString(
186 __in_z LPCWSTR wzString,
187 __out LPWSTR* ppwzData
188 );
189HRESULT WIXAPI WcaGetIntProperty(
190 __in_z LPCWSTR wzProperty,
191 __inout int* piData
192 );
193HRESULT WIXAPI WcaGetTargetPath(
194 __in_z LPCWSTR wzFolder,
195 __out LPWSTR* ppwzData
196 );
197HRESULT WIXAPI WcaSetProperty(
198 __in_z LPCWSTR wzPropertyName,
199 __in_z LPCWSTR wzPropertyValue
200 );
201HRESULT WIXAPI WcaSetIntProperty(
202 __in_z LPCWSTR wzPropertyName,
203 __in int nPropertyValue
204 );
205BOOL WIXAPI WcaIsPropertySet(
206 __in LPCSTR szProperty
207 );
208BOOL WIXAPI WcaIsUnicodePropertySet(
209 __in LPCWSTR wzProperty
210 );
211
212HRESULT WIXAPI WcaGetRecordInteger(
213 __in MSIHANDLE hRec,
214 __in UINT uiField,
215 __inout int* piData
216 );
217HRESULT WIXAPI WcaGetRecordString(
218 __in MSIHANDLE hRec,
219 __in UINT uiField,
220 __inout LPWSTR* ppwzData
221 );
222HRESULT WIXAPI WcaGetRecordFormattedInteger(
223 __in MSIHANDLE hRec,
224 __in UINT uiField,
225 __out int* piData
226 );
227HRESULT WIXAPI WcaGetRecordFormattedString(
228 __in MSIHANDLE hRec,
229 __in UINT uiField,
230 __inout LPWSTR* ppwzData
231 );
232
233HRESULT WIXAPI WcaAllocStream(
234 __deref_out_bcount_part(cbData, 0) BYTE** ppbData,
235 __in SIZE_T cbData
236 );
237HRESULT WIXAPI WcaFreeStream(
238 __in BYTE* pbData
239 );
240
241HRESULT WIXAPI WcaGetRecordStream(
242 __in MSIHANDLE hRecBinary,
243 __in UINT uiField,
244 __deref_out_bcount_full(*pcbData) BYTE** ppbData,
245 __out DWORD* pcbData
246 );
247HRESULT WIXAPI WcaSetRecordString(
248 __in MSIHANDLE hRec,
249 __in UINT uiField,
250 __in_z LPCWSTR wzData
251 );
252HRESULT WIXAPI WcaSetRecordInteger(
253 __in MSIHANDLE hRec,
254 __in UINT uiField,
255 __in int iValue
256 );
257
258HRESULT WIXAPI WcaDoDeferredAction(
259 __in_z LPCWSTR wzAction,
260 __in_z LPCWSTR wzCustomActionData,
261 __in UINT uiCost
262 );
263DWORD WIXAPI WcaCountOfCustomActionDataRecords(
264 __in_z LPCWSTR wzData
265 );
266
267HRESULT WIXAPI WcaReadStringFromCaData(
268 __deref_in LPWSTR* ppwzCustomActionData,
269 __deref_out_z LPWSTR* ppwzString
270 );
271HRESULT WIXAPI WcaReadIntegerFromCaData(
272 __deref_in LPWSTR* ppwzCustomActionData,
273 __out int* piResult
274 );
275HRESULT WIXAPI WcaReadStreamFromCaData(
276 __deref_in LPWSTR* ppwzCustomActionData,
277 __deref_out_bcount(*pcbData) BYTE** ppbData,
278 __out DWORD_PTR* pcbData
279 );
280HRESULT WIXAPI WcaWriteStringToCaData(
281 __in_z LPCWSTR wzString,
282 __deref_inout_z LPWSTR* ppwzCustomActionData
283 );
284HRESULT WIXAPI WcaWriteIntegerToCaData(
285 __in int i,
286 __deref_out_z_opt LPWSTR* ppwzCustomActionData
287 );
288HRESULT WIXAPI WcaWriteStreamToCaData(
289 __in_bcount(cbData) const BYTE* pbData,
290 __in SIZE_T cbData,
291 __deref_inout_z_opt LPWSTR* ppwzCustomActionData
292 );
293
294HRESULT __cdecl WcaAddTempRecord(
295 __inout MSIHANDLE* phTableView,
296 __inout MSIHANDLE* phColumns,
297 __in_z LPCWSTR wzTable,
298 __out_opt MSIDBERROR* pdbError,
299 __in UINT uiUniquifyColumn,
300 __in UINT cColumns,
301 ...
302 );
303
304HRESULT WIXAPI WcaDumpTable(
305 __in_z LPCWSTR wzTable
306 );
307
308HRESULT WIXAPI WcaDeferredActionRequiresReboot();
309BOOL WIXAPI WcaDidDeferredActionRequireReboot();
310
311HRESULT WIXAPI WcaCaScriptCreateKey(
312 __out LPWSTR* ppwzScriptKey
313 );
314
315HRESULT WIXAPI WcaCaScriptCreate(
316 __in WCA_ACTION action,
317 __in WCA_CASCRIPT script,
318 __in BOOL fImpersonated,
319 __in_z LPCWSTR wzScriptKey,
320 __in BOOL fAppend,
321 __out WCA_CASCRIPT_HANDLE* phScript
322 );
323
324HRESULT WIXAPI WcaCaScriptOpen(
325 __in WCA_ACTION action,
326 __in WCA_CASCRIPT script,
327 __in BOOL fImpersonated,
328 __in_z LPCWSTR wzScriptKey,
329 __out WCA_CASCRIPT_HANDLE* phScript
330 );
331
332void WIXAPI WcaCaScriptClose(
333 __in_opt WCA_CASCRIPT_HANDLE hScript,
334 __in WCA_CASCRIPT_CLOSE closeOperation
335 );
336
337HRESULT WIXAPI WcaCaScriptReadAsCustomActionData(
338 __in WCA_CASCRIPT_HANDLE hScript,
339 __out LPWSTR* ppwzCustomActionData
340 );
341
342HRESULT WIXAPI WcaCaScriptWriteString(
343 __in WCA_CASCRIPT_HANDLE hScript,
344 __in_z LPCWSTR wzValue
345 );
346
347HRESULT WIXAPI WcaCaScriptWriteNumber(
348 __in WCA_CASCRIPT_HANDLE hScript,
349 __in DWORD dwValue
350 );
351
352void WIXAPI WcaCaScriptFlush(
353 __in WCA_CASCRIPT_HANDLE hScript
354 );
355
356void WIXAPI WcaCaScriptCleanup(
357 __in_z LPCWSTR wzProductCode,
358 __in BOOL fImpersonated
359 );
360
361HRESULT WIXAPI QuietExec(
362 __inout_z LPWSTR wzCommand,
363 __in DWORD dwTimeout,
364 __in BOOL fLogCommand,
365 __in BOOL fLogOutput
366 );
367
368HRESULT WIXAPI QuietExecCapture(
369 __inout_z LPWSTR wzCommand,
370 __in DWORD dwTimeout,
371 __in BOOL fLogCommand,
372 __in BOOL fLogOutput,
373 __out_z_opt LPWSTR* psczOutput
374 );
375
376WCA_TODO WIXAPI WcaGetComponentToDo(
377 __in_z LPCWSTR wzComponentId
378 );
379
380HRESULT WIXAPI WcaExtractBinaryToBuffer(
381 __in LPCWSTR wzBinaryId,
382 __out BYTE** pbData,
383 __out DWORD* pcbData
384 );
385HRESULT WIXAPI WcaExtractBinaryToFile(
386 __in LPCWSTR wzBinaryId,
387 __in LPCWSTR wzPath
388 );
389HRESULT WIXAPI WcaExtractBinaryToString(
390 __in LPCWSTR wzBinaryId,
391 __deref_out_z LPWSTR* psczOutput,
392 __out WCA_ENCODING* encoding
393 );
394
395#ifdef __cplusplus
396}
397#endif
diff --git a/src/libs/wcautil/WixToolset.WcaUtil/inc/wcawow64.h b/src/libs/wcautil/WixToolset.WcaUtil/inc/wcawow64.h
new file mode 100644
index 00000000..dd55f3fd
--- /dev/null
+++ b/src/libs/wcautil/WixToolset.WcaUtil/inc/wcawow64.h
@@ -0,0 +1,20 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#include "wcautil.h"
6
7#ifdef __cplusplus
8extern "C" {
9#endif
10
11HRESULT WIXAPI WcaInitializeWow64();
12BOOL WIXAPI WcaIsWow64Process();
13BOOL WIXAPI WcaIsWow64Initialized();
14HRESULT WIXAPI WcaDisableWow64FSRedirection();
15HRESULT WIXAPI WcaRevertWow64FSRedirection();
16HRESULT WIXAPI WcaFinalizeWow64();
17
18#ifdef __cplusplus
19}
20#endif
diff --git a/src/libs/wcautil/WixToolset.WcaUtil/inc/wcawrapquery.h b/src/libs/wcautil/WixToolset.WcaUtil/inc/wcawrapquery.h
new file mode 100644
index 00000000..e08f1c3f
--- /dev/null
+++ b/src/libs/wcautil/WixToolset.WcaUtil/inc/wcawrapquery.h
@@ -0,0 +1,130 @@
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 "wcautil.h"
6
7// Enumerations
8typedef enum eWrapQueryAction
9{
10 wqaTableBegin = 1,
11 wqaTableFinish,
12 wqaRowBegin,
13 wqaRowFinish
14} eWrapQueryAction;
15
16typedef enum eColumnDataType
17{
18 cdtString = 1,
19 cdtInt,
20 cdtStream,
21 cdtUnknown
22} eColumnDataType;
23
24typedef enum eFormatMaskColumn
25{
26 efmcColumn1 = 1,
27 efmcColumn2 = 1 << 1,
28 efmcColumn3 = 1 << 2,
29 efmcColumn4 = 1 << 3,
30 efmcColumn5 = 1 << 4,
31 efmcColumn6 = 1 << 5,
32 efmcColumn7 = 1 << 6,
33 efmcColumn8 = 1 << 7,
34 efmcColumn9 = 1 << 8,
35 efmcColumn10 = 1 << 9,
36 efmcColumn11 = 1 << 10,
37 efmcColumn12 = 1 << 11,
38 efmcColumn13 = 1 << 12,
39 efmcColumn14 = 1 << 13,
40 efmcColumn15 = 1 << 14,
41 efmcColumn16 = 1 << 15,
42 efmcColumn17 = 1 << 16,
43 efmcColumn18 = 1 << 17,
44 efmcColumn19 = 1 << 18,
45 efmcColumn20 = 1 << 19,
46 efmcColumn21 = 1 << 20,
47 efmcColumn22 = 1 << 21,
48 efmcColumn23 = 1 << 22,
49 efmcColumn24 = 1 << 23,
50 efmcColumn25 = 1 << 24,
51 efmcColumn26 = 1 << 25,
52 efmcColumn27 = 1 << 26,
53 efmcColumn28 = 1 << 27,
54 efmcColumn29 = 1 << 28,
55 efmcColumn30 = 1 << 29,
56 efmcColumn31 = 1 << 30,
57 efmcColumn32 = 1 << 31,
58} eFormatMaskColumn;
59
60// Keeps track of the query instance for the reading CA (deferred CA)
61typedef struct WCA_WRAPQUERY_STRUCT
62{
63 // These are used to size our dynamic arrays below
64 DWORD dwColumns, dwRows, dwNextIndex;
65
66 // Dynamic arrays of column schema information
67 eColumnDataType *pcdtColumnType;
68 LPWSTR *ppwzColumnNames;
69
70 // Dynamic array of raw record data
71 MSIHANDLE *phRecords;
72} *WCA_WRAPQUERY_HANDLE;
73
74// Wrap a query
75// Setting the pfFormatMask enables control over which fields will be formatted, and which will be left unchanged
76// Setting dwComponentColumn to something other than 0xFFFFFFFF tells WcaWrapQuery to add two additional columns to the right side of the table
77// - ISInstalled and ISAction - which map to the ComponentState of the component (the component is found in the column specified)
78// Note that if a component is NULL, the component state columns will also be left null, and it will be up to the deferred CA to fail or ignore the case appropriately
79// Setting dwDirectoryColumn to something other than 0xFFFFFFFF tells WcaWrapQuery to add two more additional columns to the right side of the table
80// - SourcePath and TargetPath - which map to the Directory's Source and Target Path (the directory is found in the column specified)
81// Note that if a directory is NULL, the directory source/target path columns will also be left null, and it will be up to the deferred CA to fail or ignore the case appropriately
82HRESULT WIXAPI WcaWrapQuery(
83 __in_z LPCWSTR pwzQuery,
84 __inout LPWSTR * ppwzCustomActionData,
85 __in_opt DWORD dwFormatMask,
86 __in_opt DWORD dwComponentColumn,
87 __in_opt DWORD dwDirectoryColumn
88 );
89// This wraps an empty table query into the custom action data - this is a way to indicate to the deferred custom action that a necessary table doesn't exist, or its query returned no results
90HRESULT WIXAPI WcaWrapEmptyQuery(
91 __inout LPWSTR * ppwzCustomActionData
92 );
93
94// Open a new unwrap query operation, with data from the ppwzCustomActionData string
95HRESULT WIXAPI WcaBeginUnwrapQuery(
96 __out WCA_WRAPQUERY_HANDLE * phWrapQuery,
97 __inout LPWSTR * ppwzCustomActionData
98 );
99
100// Get the number of records in a query being unwrapped
101DWORD WIXAPI WcaGetQueryRecords(
102 __in const WCA_WRAPQUERY_HANDLE hWrapQuery
103 );
104
105// This function resets a query back to its first row, so that the next fetch returns the first record
106void WIXAPI WcaFetchWrappedReset(
107 __in WCA_WRAPQUERY_HANDLE hWrapQuery
108 );
109// Fetch the next record in this query
110// NOTE: the MSIHANDLE returned by this function should not be released, as it is the same handle used by the query object to maintain the item.
111// so, don't use this function with PMSIHANDLE objects!
112HRESULT WIXAPI WcaFetchWrappedRecord(
113 __in WCA_WRAPQUERY_HANDLE hWrapQuery,
114 __out MSIHANDLE* phRec
115 );
116
117// Fetch the next record in the query where the string value in column dwComparisonColumn equals the value pwzExpectedValue
118// NOTE: the MSIHANDLE returned by this function should not be released, as it is the same handle used by the query object to maintain the item.
119// so, don't use this function with PMSIHANDLE objects!
120HRESULT WIXAPI WcaFetchWrappedRecordWhereString(
121 __in WCA_WRAPQUERY_HANDLE hWrapQuery,
122 __in DWORD dwComparisonColumn,
123 __in_z LPCWSTR pwzExpectedValue,
124 __out MSIHANDLE* phRec
125 );
126
127// Release a query ID (frees memory, and frees the ID for a new query)
128void WIXAPI WcaFinishUnwrapQuery(
129 __in_opt WCA_WRAPQUERY_HANDLE hWrapQuery
130 );
diff --git a/src/libs/wcautil/WixToolset.WcaUtil/packages.config b/src/libs/wcautil/WixToolset.WcaUtil/packages.config
new file mode 100644
index 00000000..aa8b4077
--- /dev/null
+++ b/src/libs/wcautil/WixToolset.WcaUtil/packages.config
@@ -0,0 +1,5 @@
1<?xml version="1.0" encoding="utf-8"?>
2<packages>
3 <package id="Nerdbank.GitVersioning" version="2.1.65" developmentDependency="true" targetFramework="net40" />
4 <package id="WixToolset.DUtil" version="4.0.72" targetFramework="native" />
5</packages> \ No newline at end of file
diff --git a/src/libs/wcautil/WixToolset.WcaUtil/precomp.h b/src/libs/wcautil/WixToolset.WcaUtil/precomp.h
new file mode 100644
index 00000000..1d41337a
--- /dev/null
+++ b/src/libs/wcautil/WixToolset.WcaUtil/precomp.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
5#include <windows.h>
6#include <msiquery.h>
7#include <wchar.h>
8#include <strsafe.h>
9
10const WCHAR MAGIC_MULTISZ_DELIM = 128;
11
12#include "wcautil.h"
13#include "inc\wcalog.h"
14#include "inc\wcawow64.h"
15#include "inc\wcawrapquery.h"
16#include "wiutil.h"
17#include "fileutil.h"
18#include "memutil.h"
19#include "strutil.h"
diff --git a/src/libs/wcautil/WixToolset.WcaUtil/qtexec.cpp b/src/libs/wcautil/WixToolset.WcaUtil/qtexec.cpp
new file mode 100644
index 00000000..19abfaf8
--- /dev/null
+++ b/src/libs/wcautil/WixToolset.WcaUtil/qtexec.cpp
@@ -0,0 +1,340 @@
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#define ONEMINUTE 60000
7
8static HRESULT CreatePipes(
9 __out HANDLE *phOutRead,
10 __out HANDLE *phOutWrite,
11 __out HANDLE *phErrWrite,
12 __out HANDLE *phInRead,
13 __out HANDLE *phInWrite
14 )
15{
16 Assert(phOutRead);
17 Assert(phOutWrite);
18 Assert(phErrWrite);
19 Assert(phInRead);
20 Assert(phInWrite);
21
22 HRESULT hr = S_OK;
23 SECURITY_ATTRIBUTES sa;
24 HANDLE hOutTemp = INVALID_HANDLE_VALUE;
25 HANDLE hInTemp = INVALID_HANDLE_VALUE;
26
27 HANDLE hOutRead = INVALID_HANDLE_VALUE;
28 HANDLE hOutWrite = INVALID_HANDLE_VALUE;
29 HANDLE hErrWrite = INVALID_HANDLE_VALUE;
30 HANDLE hInRead = INVALID_HANDLE_VALUE;
31 HANDLE hInWrite = INVALID_HANDLE_VALUE;
32
33 // Fill out security structure so we can inherit handles
34 ::ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));
35 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
36 sa.bInheritHandle = TRUE;
37 sa.lpSecurityDescriptor = NULL;
38
39 // Create pipes
40 if (!::CreatePipe(&hOutTemp, &hOutWrite, &sa, 0))
41 {
42 ExitOnLastError(hr, "Failed to create output pipe");
43 }
44
45 if (!::CreatePipe(&hInRead, &hInTemp, &sa, 0))
46 {
47 ExitOnLastError(hr, "Failed to create input pipe");
48 }
49
50
51 // Duplicate output pipe so standard error and standard output write to
52 // the same pipe
53 if (!::DuplicateHandle(::GetCurrentProcess(), hOutWrite, ::GetCurrentProcess(), &hErrWrite, 0, TRUE, DUPLICATE_SAME_ACCESS))
54 {
55 ExitOnLastError(hr, "Failed to duplicate write handle");
56 }
57
58 // We need to create new output read and input write handles that are
59 // non inheritable. Otherwise it creates handles that can't be closed.
60 if (!::DuplicateHandle(::GetCurrentProcess(), hOutTemp, ::GetCurrentProcess(), &hOutRead, 0, FALSE, DUPLICATE_SAME_ACCESS))
61 {
62 ExitOnLastError(hr, "Failed to duplicate output pipe");
63 }
64
65 if (!::DuplicateHandle(::GetCurrentProcess(), hInTemp, ::GetCurrentProcess(), &hInWrite, 0, FALSE, DUPLICATE_SAME_ACCESS))
66 {
67 ExitOnLastError(hr, "Failed to duplicate input pipe");
68 }
69
70 // now that everything has succeeded, assign to the outputs
71 *phOutRead = hOutRead;
72 hOutRead = INVALID_HANDLE_VALUE;
73
74 *phOutWrite = hOutWrite;
75 hOutWrite = INVALID_HANDLE_VALUE;
76
77 *phErrWrite = hErrWrite;
78 hErrWrite = INVALID_HANDLE_VALUE;
79
80 *phInRead = hInRead;
81 hInRead = INVALID_HANDLE_VALUE;
82
83 *phInWrite = hInWrite;
84 hInWrite = INVALID_HANDLE_VALUE;
85
86LExit:
87 ReleaseFile(hOutRead);
88 ReleaseFile(hOutWrite);
89 ReleaseFile(hErrWrite);
90 ReleaseFile(hInRead);
91 ReleaseFile(hInWrite);
92 ReleaseFile(hOutTemp);
93 ReleaseFile(hInTemp);
94
95 return hr;
96}
97
98static HRESULT HandleOutput(
99 __in BOOL fLogOutput,
100 __in HANDLE hRead,
101 __out_z_opt LPWSTR* psczOutput
102 )
103{
104 BYTE* pBuffer = NULL;
105 LPWSTR szLog = NULL;
106 LPWSTR szTemp = NULL;
107 LPWSTR pEnd = NULL;
108 LPWSTR pNext = NULL;
109 LPWSTR sczEscaped = NULL;
110 LPSTR szWrite = NULL;
111 DWORD dwBytes = OUTPUT_BUFFER;
112 BOOL bFirst = TRUE;
113 BOOL bUnicode = TRUE;
114 HRESULT hr = S_OK;
115
116 // Get buffer for output
117 pBuffer = static_cast<BYTE *>(MemAlloc(OUTPUT_BUFFER, FALSE));
118 ExitOnNull(pBuffer, hr, E_OUTOFMEMORY, "Failed to allocate buffer for output.");
119
120 while (0 != dwBytes)
121 {
122 ::ZeroMemory(pBuffer, OUTPUT_BUFFER);
123 if (!::ReadFile(hRead, pBuffer, OUTPUT_BUFFER - 1, &dwBytes, NULL) && GetLastError() != ERROR_BROKEN_PIPE)
124 {
125 ExitOnLastError(hr, "Failed to read from handle.");
126 }
127
128 if (fLogOutput)
129 {
130 // Check for UNICODE or ANSI output
131 if (bFirst)
132 {
133 if ((isgraph(pBuffer[0]) && isgraph(pBuffer[1])) ||
134 (isgraph(pBuffer[0]) && isspace(pBuffer[1])) ||
135 (isspace(pBuffer[0]) && isgraph(pBuffer[1])) ||
136 (isspace(pBuffer[0]) && isspace(pBuffer[1])))
137 {
138 bUnicode = FALSE;
139 }
140
141 bFirst = FALSE;
142 }
143
144 // Keep track of output
145 if (bUnicode)
146 {
147 hr = StrAllocConcat(&szLog, (LPCWSTR)pBuffer, 0);
148 ExitOnFailure(hr, "Failed to concatenate output strings.");
149
150 if (psczOutput)
151 {
152 hr = StrAllocConcat(psczOutput, (LPCWSTR)pBuffer, 0);
153 ExitOnFailure(hr, "Failed to concatenate output to return string.");
154 }
155 }
156 else
157 {
158 hr = StrAllocStringAnsi(&szTemp, (LPCSTR)pBuffer, 0, CP_OEMCP);
159 ExitOnFailure(hr, "Failed to allocate output string.");
160 hr = StrAllocConcat(&szLog, szTemp, 0);
161 ExitOnFailure(hr, "Failed to concatenate output strings.");
162
163 if (psczOutput)
164 {
165 hr = StrAllocConcat(psczOutput, szTemp, 0);
166 ExitOnFailure(hr, "Failed to concatenate output to return string.");
167 }
168 }
169
170 // Log each line of the output
171 pNext = szLog;
172 pEnd = wcschr(szLog, L'\r');
173 if (NULL == pEnd)
174 {
175 pEnd = wcschr(szLog, L'\n');
176 }
177 while (pEnd && *pEnd)
178 {
179 // Find beginning of next line
180 pEnd[0] = 0;
181 ++pEnd;
182 if ((pEnd[0] == L'\r') || (pEnd[0] == L'\n'))
183 {
184 ++pEnd;
185 }
186
187 // Log output
188 hr = StrAllocString(&sczEscaped, pNext, 0);
189 ExitOnFailure(hr, "Failed to allocate copy of string");
190
191 hr = StrReplaceStringAll(&sczEscaped, L"%", L"%%");
192 ExitOnFailure(hr, "Failed to escape percent signs in string");
193
194 hr = StrAnsiAllocString(&szWrite, sczEscaped, 0, CP_OEMCP);
195 ExitOnFailure(hr, "Failed to convert output to ANSI");
196 WcaLog(LOGMSG_STANDARD, szWrite);
197
198 // Next line
199 pNext = pEnd;
200 pEnd = wcschr(pNext, L'\r');
201 if (NULL == pEnd)
202 {
203 pEnd = wcschr(pNext, L'\n');
204 }
205 }
206
207 hr = StrAllocString(&szTemp, pNext, 0);
208 ExitOnFailure(hr, "Failed to allocate string");
209
210 hr = StrAllocString(&szLog, szTemp, 0);
211 ExitOnFailure(hr, "Failed to allocate string");
212 }
213 }
214
215 // Print any text that didn't end with a new line
216 if (szLog && *szLog)
217 {
218 hr = StrReplaceStringAll(&szLog, L"%", L"%%");
219 ExitOnFailure(hr, "Failed to escape percent signs in string");
220
221 hr = StrAnsiAllocString(&szWrite, szLog, 0, CP_OEMCP);
222 ExitOnFailure(hr, "Failed to convert output to ANSI");
223
224 WcaLog(LOGMSG_VERBOSE, szWrite);
225 }
226
227LExit:
228 ReleaseMem(pBuffer);
229
230 ReleaseStr(szLog);
231 ReleaseStr(szTemp);
232 ReleaseStr(szWrite);
233 ReleaseStr(sczEscaped);
234
235 return hr;
236}
237
238static HRESULT QuietExecImpl(
239 __inout_z LPWSTR wzCommand,
240 __in DWORD dwTimeout,
241 __in BOOL fLogCommand,
242 __in BOOL fLogOutput,
243 __out_z_opt LPWSTR* psczOutput
244 )
245{
246 HRESULT hr = S_OK;
247 PROCESS_INFORMATION oProcInfo;
248 STARTUPINFOW oStartInfo;
249 DWORD dwExitCode = ERROR_SUCCESS;
250 HANDLE hOutRead = INVALID_HANDLE_VALUE;
251 HANDLE hOutWrite = INVALID_HANDLE_VALUE;
252 HANDLE hErrWrite = INVALID_HANDLE_VALUE;
253 HANDLE hInRead = INVALID_HANDLE_VALUE;
254 HANDLE hInWrite = INVALID_HANDLE_VALUE;
255
256 memset(&oProcInfo, 0, sizeof(oProcInfo));
257 memset(&oStartInfo, 0, sizeof(oStartInfo));
258
259 // Create output redirect pipes
260 hr = CreatePipes(&hOutRead, &hOutWrite, &hErrWrite, &hInRead, &hInWrite);
261 ExitOnFailure(hr, "Failed to create output pipes");
262
263 // Set up startup structure
264 oStartInfo.cb = sizeof(STARTUPINFOW);
265 oStartInfo.dwFlags = STARTF_USESTDHANDLES;
266 oStartInfo.hStdInput = hInRead;
267 oStartInfo.hStdOutput = hOutWrite;
268 oStartInfo.hStdError = hErrWrite;
269
270 // Log command if we were asked to do so
271 if (fLogCommand)
272 {
273 WcaLog(LOGMSG_VERBOSE, "%ls", wzCommand);
274 }
275
276#pragma prefast(suppress:25028)
277 if (::CreateProcessW(NULL,
278 wzCommand, // command line
279 NULL, // security info
280 NULL, // thread info
281 TRUE, // inherit handles
282 ::GetPriorityClass(::GetCurrentProcess()) | CREATE_NO_WINDOW, // creation flags
283 NULL, // environment
284 NULL, // cur dir
285 &oStartInfo,
286 &oProcInfo))
287 {
288 ReleaseFile(oProcInfo.hThread);
289
290 // Close child output/input handles so it doesn't hang
291 ReleaseFile(hOutWrite);
292 ReleaseFile(hErrWrite);
293 ReleaseFile(hInRead);
294
295 // Log output if we were asked to do so; otherwise just read the output handle
296 HandleOutput(fLogOutput, hOutRead, psczOutput);
297
298 // Wait for everything to finish
299 ::WaitForSingleObject(oProcInfo.hProcess, dwTimeout);
300 if (!::GetExitCodeProcess(oProcInfo.hProcess, &dwExitCode))
301 {
302 dwExitCode = ERROR_SEM_IS_SET;
303 }
304
305 ReleaseFile(hOutRead);
306 ReleaseFile(hInWrite);
307 ReleaseFile(oProcInfo.hProcess);
308 }
309 else
310 {
311 ExitOnLastError(hr, "Command failed to execute.");
312 }
313
314 ExitOnWin32Error(dwExitCode, hr, "Command line returned an error.");
315
316LExit:
317 return hr;
318}
319
320
321HRESULT WIXAPI QuietExec(
322 __inout_z LPWSTR wzCommand,
323 __in DWORD dwTimeout,
324 __in BOOL fLogCommand,
325 __in BOOL fLogOutput
326 )
327{
328 return QuietExecImpl(wzCommand, dwTimeout, fLogCommand, fLogOutput, NULL);
329}
330
331HRESULT WIXAPI QuietExecCapture(
332 __inout_z LPWSTR wzCommand,
333 __in DWORD dwTimeout,
334 __in BOOL fLogCommand,
335 __in BOOL fLogOutput,
336 __out_z_opt LPWSTR* psczOutput
337 )
338{
339 return QuietExecImpl(wzCommand, dwTimeout, fLogCommand, fLogOutput, psczOutput);
340}
diff --git a/src/libs/wcautil/WixToolset.WcaUtil/wcalog.cpp b/src/libs/wcautil/WixToolset.WcaUtil/wcalog.cpp
new file mode 100644
index 00000000..cc7d1438
--- /dev/null
+++ b/src/libs/wcautil/WixToolset.WcaUtil/wcalog.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/********************************************************************
6 IsVerboseLoggingPolicy() - internal helper function to detect if
7 policy is set for verbose logging. Does
8 not require database access.
9********************************************************************/
10static BOOL IsVerboseLoggingPolicy()
11{
12 BOOL fVerbose = FALSE;
13 HKEY hkey = NULL;
14 WCHAR rgwc[16] = { 0 };
15 DWORD cb = sizeof(rgwc);
16 if (ERROR_SUCCESS == ::RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\Policies\\Microsoft\\Windows\\Installer", 0, KEY_QUERY_VALUE, &hkey))
17 {
18 if (ERROR_SUCCESS == ::RegQueryValueExW(hkey, L"Logging", 0, NULL, reinterpret_cast<BYTE*>(rgwc), &cb))
19 {
20 for (LPCWSTR pwc = rgwc; (cb / sizeof(WCHAR)) > static_cast<DWORD>(pwc - rgwc) && *pwc; pwc++)
21 {
22 if (L'v' == *pwc || L'V' == *pwc)
23 {
24 fVerbose = TRUE;
25 break;
26 }
27 }
28 }
29
30 ::RegCloseKey(hkey);
31 }
32 return fVerbose;
33}
34
35/********************************************************************
36 IsVerboseLogging() - internal helper function to detect if doing
37 verbose logging. Checks:
38 1. LOGVERBOSE property.
39 2. MsiLogging property contains 'v'
40 3. Policy from registry.
41
42 Requires database access.
43********************************************************************/
44BOOL WIXAPI IsVerboseLogging()
45{
46 static int iVerbose = -1;
47 LPWSTR pwzMsiLogging = NULL;
48
49 if (0 > iVerbose)
50 {
51 iVerbose = WcaIsPropertySet("LOGVERBOSE");
52 if (0 == iVerbose)
53 {
54 // if the property wasn't set, check the MsiLogging property (MSI 4.0+)
55 HRESULT hr = WcaGetProperty(L"MsiLogging", &pwzMsiLogging);
56 ExitOnFailure(hr, "failed to get MsiLogging property");
57
58 if (pwzMsiLogging)
59 {
60 for (int i = 0; pwzMsiLogging[i]; i++)
61 {
62 if (L'v' == pwzMsiLogging[i] || L'V' == pwzMsiLogging[i])
63 {
64 iVerbose = 1;
65 break;
66 }
67 }
68 }
69
70 // last chance: Check the registry to see if the logging policy was turned on
71 if (0 == iVerbose && IsVerboseLoggingPolicy())
72 {
73 iVerbose = 1;
74 }
75 }
76 }
77
78LExit:
79 ReleaseStr(pwzMsiLogging);
80 Assert(iVerbose >= 0);
81 return (BOOL)iVerbose;
82}
83
84/********************************************************************
85 SetVerboseLoggingAtom() - Sets one of two global Atoms to specify
86 if the install should do verbose logging.
87 Communicates the verbose setting to
88 deferred CAs.
89 Set a negative case atom so that we can
90 distinguish between an unset atom and the
91 non-verbose case. This helps prevent the
92 expensive regkey lookup for non-verbose.
93********************************************************************/
94HRESULT WIXAPI SetVerboseLoggingAtom(BOOL bValue)
95{
96 HRESULT hr = S_OK;
97 ATOM atomVerbose = 0;
98
99 atomVerbose = ::GlobalFindAtomW(L"WcaVerboseLogging");
100 if (0 == atomVerbose && bValue)
101 {
102 atomVerbose = ::GlobalAddAtomW(L"WcaVerboseLogging");
103 ExitOnNullWithLastError(atomVerbose, hr, "Failed to create WcaVerboseLogging global atom.");
104 }
105 else if (0 != atomVerbose && !bValue)
106 {
107 ::SetLastError(ERROR_SUCCESS);
108 ::GlobalDeleteAtom(atomVerbose);
109 ExitOnLastError(hr, "Failed to delete WcaVerboseLogging global atom.");
110 }
111
112 atomVerbose = ::GlobalFindAtomW(L"WcaNotVerboseLogging");
113 if (0 == atomVerbose && !bValue)
114 {
115 atomVerbose = ::GlobalAddAtomW(L"WcaNotVerboseLogging");
116 ExitOnNullWithLastError(atomVerbose, hr, "Failed to create WcaNotVerboseLogging global atom.");
117 }
118 else if (0 != atomVerbose && bValue)
119 {
120 ::SetLastError(ERROR_SUCCESS);
121 ::GlobalDeleteAtom(atomVerbose);
122 ExitOnLastError(hr, "Failed to delete WcaNotVerboseLogging global atom.");
123 }
124
125LExit:
126 return hr;
127}
128
129/********************************************************************
130 IsVerboseLoggingLite() - internal helper function to detect if atom was
131 previously set to specify verbose logging.
132 Falls back on policy for an installer that is
133 unable to set the atom (no immediate CAs).
134
135 Does not require database access.
136********************************************************************/
137static BOOL IsVerboseLoggingLite()
138{
139 ATOM atomVerbose = ::GlobalFindAtomW(L"WcaVerboseLogging");
140 if (0 != atomVerbose)
141 {
142 return TRUE;
143 }
144
145 atomVerbose = ::GlobalFindAtomW(L"WcaNotVerboseLogging");
146 if (0 != atomVerbose)
147 {
148 return FALSE;
149 }
150
151 return IsVerboseLoggingPolicy();
152}
153
154/********************************************************************
155 WcaLog() - outputs trace and log info
156
157*******************************************************************/
158extern "C" void __cdecl WcaLog(
159 __in LOGLEVEL llv,
160 __in_z __format_string PCSTR fmt,
161 ...
162 )
163{
164 static char szFmt[LOG_BUFFER];
165 static char szBuf[LOG_BUFFER];
166 static bool fInLogPrint = false;
167
168 // prevent re-entrant logprints. (recursion issues between assert/logging code)
169 if (fInLogPrint)
170 return;
171 fInLogPrint = true;
172
173 if (LOGMSG_STANDARD == llv ||
174 (LOGMSG_VERBOSE == llv && IsVerboseLoggingLite())
175#ifdef DEBUG
176 || LOGMSG_TRACEONLY == llv
177#endif
178 )
179 {
180 va_list args;
181 va_start(args, fmt);
182
183 LPCSTR szLogName = WcaGetLogName();
184 if (szLogName[0] != 0)
185 StringCchPrintfA(szFmt, countof(szFmt), "%s: %s", szLogName, fmt);
186 else
187 StringCchCopyA(szFmt, countof(szFmt), fmt);
188
189 StringCchVPrintfA(szBuf, countof(szBuf), szFmt, args);
190 va_end(args);
191
192#ifdef DEBUG
193 // always write to the log in debug
194#else
195 if (llv == LOGMSG_STANDARD || (llv == LOGMSG_VERBOSE && IsVerboseLoggingLite()))
196#endif
197 {
198 PMSIHANDLE hrec = MsiCreateRecord(1);
199
200 ::MsiRecordSetStringA(hrec, 0, szBuf);
201 // TODO: Recursion on failure. May not be safe to assert from here.
202 WcaProcessMessage(INSTALLMESSAGE_INFO, hrec);
203 }
204
205#if DEBUG
206 StringCchCatA(szBuf, countof(szBuf), "\n");
207 OutputDebugStringA(szBuf);
208#endif
209 }
210
211 fInLogPrint = false;
212 return;
213}
214
215
216/********************************************************************
217 WcaDisplayAssert() - called before Assert() dialog shows
218
219 NOTE: writes the assert string to the MSI log
220********************************************************************/
221extern "C" BOOL WIXAPI WcaDisplayAssert(
222 __in LPCSTR sz
223 )
224{
225 WcaLog(LOGMSG_STANDARD, "Debug Assert Message: %s", sz);
226 return TRUE;
227}
228
229
230/********************************************************************
231 WcaLogError() - called before ExitOnXXX() macro exits the function
232
233 NOTE: writes the hresult and error string to the MSI log
234********************************************************************/
235extern "C" void WcaLogError(
236 __in HRESULT hr,
237 __in LPCSTR szMessage,
238 ...
239 )
240{
241 va_list dots;
242
243 va_start(dots, szMessage);
244 WcaLogErrorArgs(hr, szMessage, dots);
245 va_end(dots);
246}
247
248
249/********************************************************************
250 WcaLogErrorArgs() - called before ExitOnXXX() macro exits the function
251
252 NOTE: writes the hresult and error string to the MSI log
253********************************************************************/
254extern "C" void WcaLogErrorArgs(
255 __in HRESULT hr,
256 __in LPCSTR szMessage,
257 __in va_list args
258 )
259{
260 char szBuffer[LOG_BUFFER];
261
262 StringCchVPrintfA(szBuffer, countof(szBuffer), szMessage, args);
263
264 // log the message if using Wca common layer
265 if (WcaIsInitialized())
266 {
267 WcaLog(LOGMSG_STANDARD, "Error 0x%x: %s", hr, szBuffer);
268 }
269}
diff --git a/src/libs/wcautil/WixToolset.WcaUtil/wcascript.cpp b/src/libs/wcautil/WixToolset.WcaUtil/wcascript.cpp
new file mode 100644
index 00000000..a7e98491
--- /dev/null
+++ b/src/libs/wcautil/WixToolset.WcaUtil/wcascript.cpp
@@ -0,0 +1,429 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5
6static HRESULT CaScriptFileName(
7 __in WCA_ACTION action,
8 __in WCA_CASCRIPT script,
9 __in BOOL fImpersonated,
10 __in_z LPCWSTR wzScriptKey,
11 __out LPWSTR* pwzScriptName
12 );
13
14
15/********************************************************************
16 WcaCaScriptCreateKey() - creates a unique script key for this
17 CustomAction.
18
19********************************************************************/
20extern "C" HRESULT WIXAPI WcaCaScriptCreateKey(
21 __out LPWSTR* ppwzScriptKey
22 )
23{
24 AssertSz(WcaIsInitialized(), "WcaInitialize() should have been called before calling this function.");
25 HRESULT hr = S_OK;
26
27 hr = StrAllocStringAnsi(ppwzScriptKey, WcaGetLogName(), 0, CP_ACP);
28 ExitOnFailure(hr, "Failed to create script key.");
29
30LExit:
31 return hr;
32}
33
34
35/********************************************************************
36 WcaCaScriptCreate() - creates the appropriate script for this
37 CustomAction Script Key.
38
39********************************************************************/
40extern "C" HRESULT WIXAPI WcaCaScriptCreate(
41 __in WCA_ACTION action,
42 __in WCA_CASCRIPT script,
43 __in BOOL fImpersonated,
44 __in_z LPCWSTR wzScriptKey,
45 __in BOOL fAppend,
46 __out WCA_CASCRIPT_HANDLE* phScript
47 )
48{
49 HRESULT hr = S_OK;
50 LPWSTR pwzScriptPath = NULL;
51 HANDLE hScriptFile = INVALID_HANDLE_VALUE;
52
53 hr = CaScriptFileName(action, script, fImpersonated, wzScriptKey, &pwzScriptPath);
54 ExitOnFailure(hr, "Failed to calculate script file name.");
55
56 hScriptFile = ::CreateFileW(pwzScriptPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, fAppend ? OPEN_ALWAYS : CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
57 if (INVALID_HANDLE_VALUE == hScriptFile)
58 {
59 ExitWithLastError(hr, "Failed to open CaScript: %ls", pwzScriptPath);
60 }
61
62 if (fAppend && INVALID_SET_FILE_POINTER == ::SetFilePointer(hScriptFile, 0, NULL, FILE_END))
63 {
64 ExitWithLastError(hr, "Failed to seek to end of file.");
65 }
66
67 *phScript = static_cast<WCA_CASCRIPT_HANDLE>(MemAlloc(sizeof(WCA_CASCRIPT_STRUCT), TRUE));
68 ExitOnNull(*phScript, hr, E_OUTOFMEMORY, "Failed to allocate space for cascript handle.");
69
70 (*phScript)->pwzScriptPath = pwzScriptPath;
71 pwzScriptPath = NULL;
72 (*phScript)->hScriptFile = hScriptFile;
73 hScriptFile = INVALID_HANDLE_VALUE;
74
75LExit:
76 if (INVALID_HANDLE_VALUE != hScriptFile)
77 {
78 ::CloseHandle(hScriptFile);
79 }
80
81 ReleaseStr(pwzScriptPath);
82 return hr;
83}
84
85
86/********************************************************************
87 WcaCaScriptOpen() - opens the appropriate script for this CustomAction
88 Script Key.
89
90********************************************************************/
91extern "C" HRESULT WIXAPI WcaCaScriptOpen(
92 __in WCA_ACTION action,
93 __in WCA_CASCRIPT script,
94 __in BOOL fImpersonated,
95 __in_z LPCWSTR wzScriptKey,
96 __out WCA_CASCRIPT_HANDLE* phScript
97 )
98{
99 HRESULT hr = S_OK;
100 LPWSTR pwzScriptPath = NULL;
101 HANDLE hScriptFile = INVALID_HANDLE_VALUE;
102
103 hr = CaScriptFileName(action, script, fImpersonated, wzScriptKey, &pwzScriptPath);
104 ExitOnFailure(hr, "Failed to calculate script file name.");
105
106 hScriptFile = ::CreateFileW(pwzScriptPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
107 if (INVALID_HANDLE_VALUE == hScriptFile)
108 {
109 ExitWithLastError(hr, "Failed to open CaScript: %ls", pwzScriptPath);
110 }
111
112 *phScript = static_cast<WCA_CASCRIPT_HANDLE>(MemAlloc(sizeof(WCA_CASCRIPT_STRUCT), TRUE));
113 ExitOnNull(*phScript, hr, E_OUTOFMEMORY, "Failed to allocate space for cascript handle.");
114
115 (*phScript)->pwzScriptPath = pwzScriptPath;
116 pwzScriptPath = NULL;
117 (*phScript)->hScriptFile = hScriptFile;
118 hScriptFile = INVALID_HANDLE_VALUE;
119
120LExit:
121 if (INVALID_HANDLE_VALUE != hScriptFile)
122 {
123 ::CloseHandle(hScriptFile);
124 }
125
126 ReleaseStr(pwzScriptPath);
127 return hr;
128}
129
130
131/********************************************************************
132 WcaCaScriptClose() - closes an open script handle.
133
134********************************************************************/
135extern "C" void WIXAPI WcaCaScriptClose(
136 __in_opt WCA_CASCRIPT_HANDLE hScript,
137 __in WCA_CASCRIPT_CLOSE closeOperation
138 )
139{
140 if (hScript)
141 {
142 if (INVALID_HANDLE_VALUE != hScript->hScriptFile)
143 {
144 ::CloseHandle(hScript->hScriptFile);
145 }
146
147 if (hScript->pwzScriptPath)
148 {
149 if (WCA_CASCRIPT_CLOSE_DELETE == closeOperation)
150 {
151 ::DeleteFileW(hScript->pwzScriptPath);
152 }
153
154 StrFree(hScript->pwzScriptPath);
155 }
156
157 MemFree(hScript);
158 }
159}
160
161
162/********************************************************************
163 WcaCaScriptReadAsCustomActionData() - read the ca script into a format
164 that is useable by other CA data
165 functions.
166
167********************************************************************/
168extern "C" HRESULT WIXAPI WcaCaScriptReadAsCustomActionData(
169 __in WCA_CASCRIPT_HANDLE hScript,
170 __out LPWSTR* ppwzCustomActionData
171 )
172{
173 HRESULT hr = S_OK;
174 LARGE_INTEGER liScriptSize = { 0 };
175 BYTE* pbData = NULL;
176 DWORD cbData = 0;
177
178 if (!::GetFileSizeEx(hScript->hScriptFile, &liScriptSize))
179 {
180 ExitWithLastError(hr, "Failed to get size of ca script file.");
181 }
182
183 if (0 != liScriptSize.HighPart || 0 != (liScriptSize.LowPart % sizeof(WCHAR)))
184 {
185 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
186 ExitOnRootFailure(hr, "Invalid data read from ca script.");
187 }
188
189 cbData = liScriptSize.LowPart;
190 if (cbData)
191 {
192 pbData = static_cast<BYTE*>(MemAlloc(cbData, TRUE));
193 ExitOnNull(pbData, hr, E_OUTOFMEMORY, "Failed to allocate memory to read in ca script.");
194
195 if (INVALID_SET_FILE_POINTER == ::SetFilePointer(hScript->hScriptFile, 0, NULL, FILE_BEGIN))
196 {
197 ExitWithLastError(hr, "Failed to reset to beginning of ca script.");
198 }
199
200 DWORD cbTotalRead = 0;
201 DWORD cbRead = 0;
202 do
203 {
204 if (!::ReadFile(hScript->hScriptFile, pbData + cbTotalRead, cbData - cbTotalRead, &cbRead, NULL))
205 {
206 ExitWithLastError(hr, "Failed to read from ca script.");
207 }
208
209 cbTotalRead += cbRead;
210 } while (cbRead && cbTotalRead < cbData);
211
212 if (cbTotalRead != cbData)
213 {
214 hr = E_UNEXPECTED;
215 ExitOnFailure(hr, "Failed to completely read ca script.");
216 }
217 }
218
219 // Add one to the allocated space because the data stored in the script is not
220 // null terminated. After copying the memory over, we'll ensure the string is
221 // null terminated.
222 DWORD cchData = cbData / sizeof(WCHAR) + 1;
223 hr = StrAlloc(ppwzCustomActionData, cchData);
224 ExitOnFailure(hr, "Failed to copy ca script.");
225
226 if (cbData)
227 {
228 CopyMemory(*ppwzCustomActionData, pbData, cbData);
229 }
230
231 (*ppwzCustomActionData)[cchData - 1] = L'\0';
232
233LExit:
234 ReleaseMem(pbData);
235 return hr;
236}
237
238
239/********************************************************************
240 WcaCaScriptWriteString() - writes a string to the ca script.
241
242********************************************************************/
243extern "C" HRESULT WIXAPI WcaCaScriptWriteString(
244 __in WCA_CASCRIPT_HANDLE hScript,
245 __in_z LPCWSTR wzValue
246 )
247{
248 HRESULT hr = S_OK;
249 DWORD cbFile = 0;
250 WCHAR delim[] = { MAGIC_MULTISZ_DELIM }; // magic char followed by NULL terminator
251 SIZE_T cch = 0;
252
253 cbFile = ::SetFilePointer(hScript->hScriptFile, 0, NULL, FILE_END);
254 if (INVALID_SET_FILE_POINTER == cbFile)
255 {
256 ExitWithLastError(hr, "Failed to move file pointer to end of file.");
257 }
258
259 // If there is existing data in the file, append on the magic delimeter
260 // before adding our new data on the end of the file.
261 if (0 < cbFile)
262 {
263 hr = FileWriteHandle(hScript->hScriptFile, reinterpret_cast<LPCBYTE>(delim), sizeof(delim));
264 ExitOnFailure(hr, "Failed to write data to ca script.");
265 }
266
267 hr = ::StringCchLengthW(wzValue, STRSAFE_MAX_CCH, reinterpret_cast<size_t*>(&cch));
268 ExitOnRootFailure(hr, "Failed to get length of ca script string.");
269
270 hr = FileWriteHandle(hScript->hScriptFile, reinterpret_cast<LPCBYTE>(wzValue), static_cast<DWORD>(cch) * sizeof(WCHAR));
271 ExitOnFailure(hr, "Failed to write data to ca script.");
272
273LExit:
274 return hr;
275}
276
277
278/********************************************************************
279 WcaCaScriptWriteNumber() - writes a number to the ca script.
280
281********************************************************************/
282extern "C" HRESULT WIXAPI WcaCaScriptWriteNumber(
283 __in WCA_CASCRIPT_HANDLE hScript,
284 __in DWORD dwValue
285 )
286{
287 HRESULT hr = S_OK;
288 WCHAR wzBuffer[13] = { 0 };
289
290 hr = ::StringCchPrintfW(wzBuffer, countof(wzBuffer), L"%u", dwValue);
291 ExitOnFailure(hr, "Failed to convert number into string.");
292
293 hr = WcaCaScriptWriteString(hScript, wzBuffer);
294 ExitOnFailure(hr, "Failed to write number to script.");
295
296LExit:
297 return hr;
298}
299
300
301/********************************************************************
302 WcaCaScriptFlush() - best effort function to get script written to
303 disk.
304
305********************************************************************/
306extern "C" void WIXAPI WcaCaScriptFlush(
307 __in WCA_CASCRIPT_HANDLE hScript
308 )
309{
310 ::FlushFileBuffers(hScript->hScriptFile);
311}
312
313
314/********************************************************************
315 WcaCaScriptCleanup() - best effort clean-up of any cascripts left
316 over from this install/uninstall.
317
318********************************************************************/
319extern "C" void WIXAPI WcaCaScriptCleanup(
320 __in_z LPCWSTR wzProductCode,
321 __in BOOL fImpersonated
322 )
323{
324 HRESULT hr = S_OK;
325 WCHAR wzTempPath[MAX_PATH];
326 LPWSTR pwzWildCardPath = NULL;
327 WIN32_FIND_DATAW fd = { 0 };
328 HANDLE hff = INVALID_HANDLE_VALUE;
329 LPWSTR pwzDeletePath = NULL;
330
331 if (fImpersonated)
332 {
333 if (!::GetTempPathW(countof(wzTempPath), wzTempPath))
334 {
335 ExitWithLastError(hr, "Failed to get temp path.");
336 }
337 }
338 else
339 {
340 if (!::GetWindowsDirectoryW(wzTempPath, countof(wzTempPath)))
341 {
342 ExitWithLastError(hr, "Failed to get windows path.");
343 }
344
345 hr = ::StringCchCatW(wzTempPath, countof(wzTempPath), L"\\Installer\\");
346 ExitOnFailure(hr, "Failed to concat Installer directory on windows path string.");
347 }
348
349 hr = StrAllocFormatted(&pwzWildCardPath, L"%swix%s.*.???", wzTempPath, wzProductCode);
350 ExitOnFailure(hr, "Failed to allocate wildcard path to ca scripts.");
351
352 hff = ::FindFirstFileW(pwzWildCardPath, &fd);
353 if (INVALID_HANDLE_VALUE == hff)
354 {
355 ExitWithLastError(hr, "Failed to find files with pattern: %ls", pwzWildCardPath);
356 }
357
358 do
359 {
360 hr = StrAllocFormatted(&pwzDeletePath, L"%s%s", wzTempPath, fd.cFileName);
361 if (SUCCEEDED(hr))
362 {
363 if (!::DeleteFileW(pwzDeletePath))
364 {
365 DWORD er = ::GetLastError();
366 WcaLog(LOGMSG_VERBOSE, "Failed to clean up CAScript file: %ls, er: %d", fd.cFileName, er);
367 }
368 }
369 else
370 {
371 WcaLog(LOGMSG_VERBOSE, "Failed to allocate path to clean up CAScript file: %ls, hr: 0x%x", fd.cFileName, hr);
372 }
373 } while(::FindNextFileW(hff, &fd));
374
375LExit:
376 if (INVALID_HANDLE_VALUE == hff)
377 {
378 ::FindClose(hff);
379 }
380
381 ReleaseStr(pwzDeletePath);
382 ReleaseStr(pwzWildCardPath);
383 return;
384}
385
386
387static HRESULT CaScriptFileName(
388 __in WCA_ACTION action,
389 __in WCA_CASCRIPT script,
390 __in BOOL fImpersonated,
391 __in_z LPCWSTR wzScriptKey,
392 __out LPWSTR* ppwzScriptName
393 )
394{
395 HRESULT hr = S_OK;
396 WCHAR wzTempPath[MAX_PATH];
397 LPWSTR pwzProductCode = NULL;
398 WCHAR chInstallOrUninstall = action == WCA_ACTION_INSTALL ? L'i' : L'u';
399 WCHAR chScheduledOrRollback = script == WCA_CASCRIPT_SCHEDULED ? L's' : L'r';
400 WCHAR chUserOrMachine = fImpersonated ? L'u' : L'm';
401
402 if (fImpersonated)
403 {
404 if (!::GetTempPathW(countof(wzTempPath), wzTempPath))
405 {
406 ExitWithLastError(hr, "Failed to get temp path.");
407 }
408 }
409 else
410 {
411 if (!::GetWindowsDirectoryW(wzTempPath, countof(wzTempPath)))
412 {
413 ExitWithLastError(hr, "Failed to get windows path.");
414 }
415
416 hr = ::StringCchCatW(wzTempPath, countof(wzTempPath), L"\\Installer\\");
417 ExitOnFailure(hr, "Failed to concat Installer directory on windows path string.");
418 }
419
420 hr = WcaGetProperty(L"ProductCode", &pwzProductCode);
421 ExitOnFailure(hr, "Failed to get ProductCode.");
422
423 hr = StrAllocFormatted(ppwzScriptName, L"%swix%s.%s.%c%c%c", wzTempPath, pwzProductCode, wzScriptKey, chScheduledOrRollback, chUserOrMachine, chInstallOrUninstall);
424 ExitOnFailure(hr, "Failed to allocate path to ca script.");
425
426LExit:
427 ReleaseStr(pwzProductCode);
428 return hr;
429}
diff --git a/src/libs/wcautil/WixToolset.WcaUtil/wcautil.cpp b/src/libs/wcautil/WixToolset.WcaUtil/wcautil.cpp
new file mode 100644
index 00000000..11867d10
--- /dev/null
+++ b/src/libs/wcautil/WixToolset.WcaUtil/wcautil.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
5// globals
6HMODULE g_hInstCADLL;
7
8// statics
9static BOOL s_fInitialized;
10static MSIHANDLE s_hInstall;
11static MSIHANDLE s_hDatabase;
12static char s_szCustomActionLogName[32];
13static UINT s_iRetVal;
14
15static void CALLBACK WcaTraceError(
16 __in_z LPCSTR szFile,
17 __in int iLine,
18 __in REPORT_LEVEL rl,
19 __in UINT source,
20 __in HRESULT hrError,
21 __in_z __format_string LPCSTR szFormat,
22 __in va_list args
23 );
24
25/********************************************************************
26 WcaGlobalInitialize() - initializes the Wca library, should be
27 called once per custom action Dll during
28 DllMain on DLL_PROCESS_ATTACH
29
30********************************************************************/
31extern "C" void WIXAPI WcaGlobalInitialize(
32 __in HINSTANCE hInst
33 )
34{
35 g_hInstCADLL = hInst;
36 DutilInitialize(&WcaTraceError);
37 MemInitialize();
38
39 AssertSetModule(g_hInstCADLL);
40 AssertSetDisplayFunction(WcaDisplayAssert);
41}
42
43
44/********************************************************************
45 WcaGlobalFinalize() - finalizes the Wca library, should be the
46 called once per custom action Dll during
47 DllMain on DLL_PROCESS_DETACH
48
49********************************************************************/
50extern "C" void WIXAPI WcaGlobalFinalize()
51{
52#ifdef DEBUG
53 if (WcaIsInitialized())
54 {
55 CHAR szBuf[2048];
56 StringCchPrintfA(szBuf, countof(szBuf), "CustomAction %s called WcaInitialize() but not WcaFinalize()", WcaGetLogName());
57
58 AssertSz(FALSE, szBuf);
59 }
60#endif
61 MemUninitialize();
62 DutilUninitialize();
63 g_hInstCADLL = NULL;
64}
65
66
67/********************************************************************
68 WcaInitialize() - initializes the Wca framework, should be the first
69 thing called by all CustomActions
70
71********************************************************************/
72extern "C" HRESULT WIXAPI WcaInitialize(
73 __in MSIHANDLE hInstall,
74 __in_z PCSTR szCustomActionLogName
75 )
76{
77 WCHAR wzCAFileName[MAX_PATH] = {0};
78 DWORD dwMajorVersion = 0;
79 DWORD dwMinorVersion = 0;
80
81 // these statics should be called once per CustomAction invocation.
82 // Darwin does doesn't preserve DLL state across CustomAction calls so
83 // these should always be initialized to NULL. If that behavior changes
84 // we would need to do a careful review of all of our module/global data.
85 AssertSz(!s_fInitialized, "WcaInitialize() should only be called once per CustomAction");
86 Assert(NULL == s_hInstall);
87 Assert(NULL == s_hDatabase);
88 Assert(0 == *s_szCustomActionLogName);
89
90 HRESULT hr = S_OK;
91
92 s_fInitialized = TRUE;
93 s_iRetVal = ERROR_SUCCESS; // assume all will go well
94
95 s_hInstall = hInstall;
96 s_hDatabase = ::MsiGetActiveDatabase(s_hInstall); // may return null if deferred CustomAction
97
98 hr = ::StringCchCopy(s_szCustomActionLogName, countof(s_szCustomActionLogName), szCustomActionLogName);
99 ExitOnFailure(hr, "Failed to copy CustomAction log name: %s", szCustomActionLogName);
100
101 // If we got the database handle IE: immediate CA
102 if (s_hDatabase)
103 {
104 hr = SetVerboseLoggingAtom(IsVerboseLogging());
105 ExitOnFailure(hr, "Failed to set verbose logging global atom");
106 }
107
108 if (!::GetModuleFileNameW(g_hInstCADLL, wzCAFileName, countof(wzCAFileName)))
109 {
110 ExitWithLastError(hr, "Failed to get module filename");
111 }
112
113 FileVersion(wzCAFileName, &dwMajorVersion, &dwMinorVersion); // Ignore failure, just log 0.0.0.0
114
115 WcaLog(LOGMSG_VERBOSE, "Entering %s in %ls, version %u.%u.%u.%u", szCustomActionLogName, wzCAFileName, (DWORD)HIWORD(dwMajorVersion), (DWORD)LOWORD(dwMajorVersion), (DWORD)HIWORD(dwMinorVersion), (DWORD)LOWORD(dwMinorVersion));
116
117 Assert(s_hInstall);
118LExit:
119 if (FAILED(hr))
120 {
121 if (s_hDatabase)
122 {
123 ::MsiCloseHandle(s_hDatabase);
124 s_hDatabase = NULL;
125 }
126
127 s_hInstall = NULL;
128 s_fInitialized = FALSE;
129 }
130
131 return hr;
132}
133
134
135/********************************************************************
136 WcaFinalize() - cleans up after the Wca framework, should be the last
137 thing called by all CustomActions
138
139********************************************************************/
140extern "C" UINT WIXAPI WcaFinalize(
141 __in UINT iReturnValue
142 )
143{
144 AssertSz(!WcaIsWow64Initialized(), "WcaFinalizeWow64() should be called before calling WcaFinalize()");
145
146 // clean up after our initialization
147 if (s_hDatabase)
148 {
149 ::MsiCloseHandle(s_hDatabase);
150 s_hDatabase = NULL;
151 }
152
153 s_hInstall = NULL;
154 s_fInitialized = FALSE;
155
156 // if no error occurred during the processing of the CusotmAction return the passed in return value
157 // otherwise return the previous failure
158 return (ERROR_SUCCESS == s_iRetVal) ? iReturnValue : s_iRetVal;
159}
160
161
162/********************************************************************
163 WcaIsInitialized() - determines if WcaInitialize() has been called
164
165********************************************************************/
166extern "C" BOOL WIXAPI WcaIsInitialized()
167{
168 return s_fInitialized;
169}
170
171
172/********************************************************************
173 WcaGetInstallHandle() - gets the handle to the active install session
174
175********************************************************************/
176extern "C" MSIHANDLE WIXAPI WcaGetInstallHandle()
177{
178 AssertSz(s_hInstall, "WcaInitialize() should be called before attempting to access the install handle.");
179 return s_hInstall;
180}
181
182
183/********************************************************************
184 WcaGetDatabaseHandle() - gets the handle to the active database
185
186 NOTE: this function can only be used in immediate CustomActions.
187 Deferred CustomActions do not have access to the active
188 database.
189********************************************************************/
190extern "C" MSIHANDLE WIXAPI WcaGetDatabaseHandle()
191{
192 AssertSz(s_hDatabase, "WcaInitialize() should be called before attempting to access the install handle. Also note that deferred CustomActions do not have access to the active database.");
193 return s_hDatabase;
194}
195
196
197/********************************************************************
198 WcaGetLogName() - gets the name of the CustomAction used in logging
199
200********************************************************************/
201extern "C" const char* WIXAPI WcaGetLogName()
202{
203 return s_szCustomActionLogName;
204}
205
206
207/********************************************************************
208 WcaSetReturnValue() - sets the value to return from the CustomAction
209
210********************************************************************/
211extern "C" void WIXAPI WcaSetReturnValue(
212 __in UINT iReturnValue
213 )
214{
215 s_iRetVal = iReturnValue;
216}
217
218
219/********************************************************************
220 WcaCancelDetected() - determines if the user has canceled yet
221
222 NOTE: returns true when WcaSetReturnValue() is set to ERROR_INSTALL_USEREXIT
223********************************************************************/
224extern "C" BOOL WIXAPI WcaCancelDetected()
225{
226 return ERROR_INSTALL_USEREXIT == s_iRetVal;
227}
228
229static void CALLBACK WcaTraceError(
230 __in_z LPCSTR /*szFile*/,
231 __in int /*iLine*/,
232 __in REPORT_LEVEL /*rl*/,
233 __in UINT source,
234 __in HRESULT hrError,
235 __in_z __format_string LPCSTR szFormat,
236 __in va_list args
237 )
238{
239 if (DUTIL_SOURCE_DEFAULT == source)
240 {
241 WcaLogErrorArgs(hrError, szFormat, args);
242 }
243}
diff --git a/src/libs/wcautil/WixToolset.WcaUtil/wcautil.nuspec b/src/libs/wcautil/WixToolset.WcaUtil/wcautil.nuspec
new file mode 100644
index 00000000..a57a5749
--- /dev/null
+++ b/src/libs/wcautil/WixToolset.WcaUtil/wcautil.nuspec
@@ -0,0 +1,30 @@
1<?xml version="1.0"?>
2<package >
3 <metadata>
4 <id>$id$</id>
5 <version>$version$</version>
6 <authors>$authors$</authors>
7 <owners>$authors$</owners>
8 <license type="expression">MS-RL</license>
9 <projectUrl>https://github.com/wixtoolset/wcautil</projectUrl>
10 <requireLicenseAcceptance>false</requireLicenseAcceptance>
11 <description>$description$</description>
12 <copyright>$copyright$</copyright>
13 <dependencies>
14 <dependency id="WixToolset.DUtil" version="[4,5)" />
15 </dependencies>
16 </metadata>
17
18 <files>
19 <file src="build\$id$.props" target="build\" />
20 <file src="inc\*" target="build\native\include" />
21 <file src="..\..\build\$configuration$\v140\x64\wcautil.lib" target="build\native\v140\x64" />
22 <file src="..\..\build\$configuration$\v140\x86\wcautil.lib" target="build\native\v140\x86" />
23 <file src="..\..\build\$configuration$\v141\x64\wcautil.lib" target="build\native\v141\x64" />
24 <file src="..\..\build\$configuration$\v141\x86\wcautil.lib" target="build\native\v141\x86" />
25 <file src="..\..\build\$configuration$\v141\ARM64\wcautil.lib" target="build\native\v141\ARM64" />
26 <file src="..\..\build\$configuration$\v142\x64\wcautil.lib" target="build\native\v142\x64" />
27 <file src="..\..\build\$configuration$\v142\x86\wcautil.lib" target="build\native\v142\x86" />
28 <file src="..\..\build\$configuration$\v142\ARM64\wcautil.lib" target="build\native\v142\ARM64" />
29 </files>
30</package>
diff --git a/src/libs/wcautil/WixToolset.WcaUtil/wcautil.vcxproj b/src/libs/wcautil/WixToolset.WcaUtil/wcautil.vcxproj
new file mode 100644
index 00000000..6876bd5b
--- /dev/null
+++ b/src/libs/wcautil/WixToolset.WcaUtil/wcautil.vcxproj
@@ -0,0 +1,94 @@
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<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
4 <Import Project="..\..\packages\WixToolset.DUtil.4.0.72\build\WixToolset.DUtil.props" Condition="Exists('..\..\packages\WixToolset.DUtil.4.0.72\build\WixToolset.DUtil.props')" />
5 <ItemGroup Label="ProjectConfigurations">
6 <ProjectConfiguration Include="Debug|ARM64">
7 <Configuration>Debug</Configuration>
8 <Platform>ARM64</Platform>
9 </ProjectConfiguration>
10 <ProjectConfiguration Include="Debug|x64">
11 <Configuration>Debug</Configuration>
12 <Platform>x64</Platform>
13 </ProjectConfiguration>
14 <ProjectConfiguration Include="Debug|Win32">
15 <Configuration>Debug</Configuration>
16 <Platform>Win32</Platform>
17 </ProjectConfiguration>
18 <ProjectConfiguration Include="Release|ARM64">
19 <Configuration>Release</Configuration>
20 <Platform>ARM64</Platform>
21 </ProjectConfiguration>
22 <ProjectConfiguration Include="Release|Win32">
23 <Configuration>Release</Configuration>
24 <Platform>Win32</Platform>
25 </ProjectConfiguration>
26 <ProjectConfiguration Include="Release|x64">
27 <Configuration>Release</Configuration>
28 <Platform>x64</Platform>
29 </ProjectConfiguration>
30 </ItemGroup>
31 <PropertyGroup Label="Globals">
32 <ProjectGuid>{5B3714B6-3A76-463E-8595-D48DA276C512}</ProjectGuid>
33 <ConfigurationType>StaticLibrary</ConfigurationType>
34 <TargetName>wcautil</TargetName>
35 <MultiTargetLibrary>true</MultiTargetLibrary>
36 <PlatformToolset>v142</PlatformToolset>
37 <CharacterSet>MultiByte</CharacterSet>
38 <Description>WiX Toolset Custom Action native utility library</Description>
39 </PropertyGroup>
40
41 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
42 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
43 <ImportGroup Label="Shared">
44 <Import Project="..\..\packages\Nerdbank.GitVersioning.2.1.65\build\Nerdbank.GitVersioning.targets" Condition="Exists('..\..\packages\Nerdbank.GitVersioning.2.1.65\build\Nerdbank.GitVersioning.targets')" />
45 </ImportGroup>
46 <Import Project="..\NativeMultiTargeting.Build.props" />
47
48 <ItemGroup>
49 <ClCompile Include="exbinary.cpp" />
50 <ClCompile Include="wcalog.cpp" />
51 <ClCompile Include="wcascript.cpp" />
52 <ClCompile Include="wcautil.cpp">
53 <PrecompiledHeader>Create</PrecompiledHeader>
54 </ClCompile>
55 <ClCompile Include="wcawrapquery.cpp" />
56 <ClCompile Include="wcawow64.cpp" />
57 <ClCompile Include="wcawrap.cpp" />
58 <ClCompile Include="qtexec.cpp" />
59 </ItemGroup>
60
61 <ItemGroup Condition="'$(Platform)' == 'Win32'">
62 <ClInclude Include="custommsierrors.h">
63 <GenerateWixInclude>caerr.wxi</GenerateWixInclude>
64 </ClInclude>
65 </ItemGroup>
66 <ItemGroup Condition="'$(Platform)' != 'Win32'">
67 <ClInclude Include="custommsierrors.h" />
68 </ItemGroup>
69 <ItemGroup>
70 <ClInclude Include="precomp.h" />
71 <ClInclude Include="inc\wcalog.h" />
72 <ClInclude Include="inc\wcautil.h" />
73 <ClInclude Include="inc\wcawow64.h" />
74 <ClInclude Include="inc\wcawrapquery.h" />
75 </ItemGroup>
76
77 <ItemGroup>
78 <None Include="packages.config" />
79 </ItemGroup>
80
81 <Target Name="PackNativeNuget"
82 DependsOnTargets="GetBuildVersion">
83 <Exec Command='nuget pack wcautil.nuspec -OutputDirectory "$(BaseOutputPath)$(Configuration)" -Properties Configuration=$(Configuration);Id=WixToolset.WcaUtil;Version="$(BuildVersionSimple)";Authors="$(Authors)";Copyright="$(Copyright)";Description="$(Description)";Title="$(Title)"' />
84 </Target>
85
86 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
87 <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
88 <PropertyGroup>
89 <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
90 </PropertyGroup>
91 <Error Condition="!Exists('..\..\packages\Nerdbank.GitVersioning.2.1.65\build\Nerdbank.GitVersioning.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Nerdbank.GitVersioning.2.1.65\build\Nerdbank.GitVersioning.targets'))" />
92 <Error Condition="!Exists('..\..\packages\WixToolset.DUtil.4.0.72\build\WixToolset.DUtil.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\WixToolset.DUtil.4.0.72\build\WixToolset.DUtil.props'))" />
93 </Target>
94</Project> \ No newline at end of file
diff --git a/src/libs/wcautil/WixToolset.WcaUtil/wcawow64.cpp b/src/libs/wcautil/WixToolset.WcaUtil/wcawow64.cpp
new file mode 100644
index 00000000..8174c43e
--- /dev/null
+++ b/src/libs/wcautil/WixToolset.WcaUtil/wcawow64.cpp
@@ -0,0 +1,169 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5static HMODULE s_hKernel32;
6static BOOL s_fWow64Initialized;
7static BOOL (*s_pfnDisableWow64)(__out PVOID* );
8static BOOL (*s_pfnRevertWow64)(__in PVOID );
9static BOOL (*s_pfnIsWow64Process) (HANDLE, PBOOL);
10static PVOID s_Wow64FSRevertState;
11static BOOL s_fWow64FSDisabled;
12
13/********************************************************************
14 WcaInitializeWow64() - Initializes the Wow64 API
15
16********************************************************************/
17extern "C" HRESULT WIXAPI WcaInitializeWow64()
18{
19 AssertSz(WcaIsInitialized(), "WcaInitialize() should be called before calling WcaInitializeWow64()");
20 AssertSz(!WcaIsWow64Initialized(), "WcaInitializeWow64() should not be called twice without calling WcaFinalizeWow64()");
21
22 s_fWow64Initialized = FALSE;
23 HRESULT hr = S_OK;
24 s_Wow64FSRevertState = NULL;
25 s_fWow64FSDisabled = false;
26
27 // Test if we have access to the Wow64 API, and store the result in bWow64APIPresent
28 s_hKernel32 = ::GetModuleHandleW(L"kernel32.dll");
29 if (!s_hKernel32)
30 {
31 ExitWithLastError(hr, "failed to get handle to kernel32.dll");
32 }
33
34 // This will test if we have access to the Wow64 API
35 s_pfnIsWow64Process = (BOOL (*)(HANDLE, PBOOL))::GetProcAddress(s_hKernel32, "IsWow64Process");
36 if (NULL != s_pfnIsWow64Process)
37 {
38 s_pfnDisableWow64 = (BOOL (*)(PVOID *))::GetProcAddress(s_hKernel32, "Wow64DisableWow64FsRedirection");
39 // If we fail, log the error but proceed, because we may not need a particular function, or the Wow64 API at all
40 if (!s_pfnDisableWow64)
41 {
42 return S_FALSE;
43 }
44
45 s_pfnRevertWow64 = (BOOL (*)(PVOID))::GetProcAddress(s_hKernel32, "Wow64RevertWow64FsRedirection");
46 if (!s_pfnRevertWow64)
47 {
48 return S_FALSE;
49 }
50
51 if (s_pfnDisableWow64 && s_pfnRevertWow64)
52 {
53 s_fWow64Initialized = TRUE;
54 }
55 }
56 else
57 {
58 return S_FALSE;
59 }
60
61LExit:
62
63 return hr;
64}
65
66/********************************************************************
67 WcaIsWow64Process() - determines if the current process is running
68 in WOW
69
70********************************************************************/
71extern "C" BOOL WIXAPI WcaIsWow64Process()
72{
73 BOOL fIsWow64Process = FALSE;
74 if (s_fWow64Initialized)
75 {
76 if (!s_pfnIsWow64Process(GetCurrentProcess(), &fIsWow64Process))
77 {
78 // clear out the value since call failed
79 fIsWow64Process = FALSE;
80 }
81 }
82 return fIsWow64Process;
83}
84
85/********************************************************************
86 WcaIsWow64Initialized() - determines if WcaInitializeWow64() has
87 been successfully called
88
89********************************************************************/
90extern "C" BOOL WIXAPI WcaIsWow64Initialized()
91{
92 return s_fWow64Initialized;
93}
94
95/********************************************************************
96 WcaDisableWow64FSRedirection() - Disables Wow64 FS Redirection
97
98********************************************************************/
99extern "C" HRESULT WIXAPI WcaDisableWow64FSRedirection()
100{
101 AssertSz(s_fWow64Initialized && s_pfnDisableWow64 != NULL, "WcaDisableWow64FSRedirection() called, but Wow64 API was not initialized");
102
103#ifdef DEBUG
104 AssertSz(!s_fWow64FSDisabled, "You must call WcaRevertWow64FSRedirection() before calling WcaDisableWow64FSRedirection() again");
105#endif
106
107 HRESULT hr = S_OK;
108 if (s_pfnDisableWow64(&s_Wow64FSRevertState))
109 {
110 s_fWow64FSDisabled = TRUE;
111 }
112 else
113 {
114 ExitWithLastError(hr, "Failed to disable WOW64.");
115 }
116
117LExit:
118 return hr;
119}
120
121/********************************************************************
122 WcaRevertWow64FSRedirection() - Reverts Wow64 FS Redirection to its
123 pre-disabled state
124
125********************************************************************/
126extern "C" HRESULT WIXAPI WcaRevertWow64FSRedirection()
127{
128 AssertSz(s_fWow64Initialized && s_pfnDisableWow64 != NULL, "WcaRevertWow64FSRedirection() called, but Wow64 API was not initialized");
129
130#ifdef DEBUG
131 AssertSz(s_fWow64FSDisabled, "You must call WcaDisableWow64FSRedirection() before calling WcaRevertWow64FSRedirection()");
132#endif
133
134 HRESULT hr = S_OK;
135 if (s_pfnRevertWow64(s_Wow64FSRevertState))
136 {
137 s_fWow64FSDisabled = FALSE;
138 }
139 else
140 {
141 ExitWithLastError(hr, "Failed to revert WOW64.");
142 }
143
144LExit:
145 return hr;
146}
147
148/********************************************************************
149 WcaFinalizeWow64() - Cleans up after Wow64 API Initialization
150
151********************************************************************/
152extern "C" HRESULT WIXAPI WcaFinalizeWow64()
153{
154 if (s_fWow64FSDisabled)
155 {
156#ifdef DEBUG
157 AssertSz(FALSE, "WcaFinalizeWow64() called while Filesystem redirection was disabled.");
158#else
159 // If we aren't in debug mode, let's do our best to recover gracefully
160 WcaRevertWow64FSRedirection();
161#endif
162 }
163
164 s_fWow64Initialized = FALSE;
165 s_pfnDisableWow64 = NULL;
166 s_pfnRevertWow64 = NULL;
167
168 return S_OK;
169}
diff --git a/src/libs/wcautil/WixToolset.WcaUtil/wcawrap.cpp b/src/libs/wcautil/WixToolset.WcaUtil/wcawrap.cpp
new file mode 100644
index 00000000..2b68f36f
--- /dev/null
+++ b/src/libs/wcautil/WixToolset.WcaUtil/wcawrap.cpp
@@ -0,0 +1,1668 @@
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/********************************************************************
7WcaProcessMessage() - sends a message from the CustomAction
8
9********************************************************************/
10extern "C" UINT WIXAPI WcaProcessMessage(
11 __in INSTALLMESSAGE eMessageType,
12 __in MSIHANDLE hRecord
13 )
14{
15 UINT er = ::MsiProcessMessage(WcaGetInstallHandle(), eMessageType, hRecord);
16 if (ERROR_INSTALL_USEREXIT == er || IDCANCEL == er)
17 {
18 WcaSetReturnValue(ERROR_INSTALL_USEREXIT);
19 }
20
21 return er;
22}
23
24
25/********************************************************************
26WcaErrorMessage() - sends an error message from the CustomAction using
27the Error table
28
29NOTE: Any and all var_args (...) must be WCHAR*
30 If you pass -1 to cArgs the count will be determined
31********************************************************************/
32extern "C" UINT __cdecl WcaErrorMessage(
33 __in int iError,
34 __in HRESULT hrError,
35 __in UINT uiType,
36 __in INT cArgs,
37 ...
38 )
39{
40 UINT er;
41 MSIHANDLE hRec = NULL;
42 va_list args = NULL;
43
44 uiType |= INSTALLMESSAGE_ERROR; // ensure error type is set
45 hRec = ::MsiCreateRecord(cArgs + 2);
46 if (!hRec)
47 {
48 er = ERROR_OUTOFMEMORY;
49 ExitOnFailure(HRESULT_FROM_WIN32(er), "failed to create record when sending error message");
50 }
51
52 er = ::MsiRecordSetInteger(hRec, 1, iError);
53 ExitOnFailure(HRESULT_FROM_WIN32(er), "failed to set error code into error message");
54
55 er = ::MsiRecordSetInteger(hRec, 2, hrError);
56 ExitOnFailure(HRESULT_FROM_WIN32(er), "failed to set hresult code into error message");
57
58 va_start(args, cArgs);
59 if (-1 == cArgs)
60 {
61 LPCWSTR wzArg = NULL;
62 va_list iter = args;
63 cArgs = 0;
64
65 while (NULL != (wzArg = va_arg(iter, WCHAR*)) && L'\0' != *wzArg)
66 {
67 ++cArgs;
68 }
69 }
70
71 for (INT i = 0; i < cArgs; i++)
72 {
73 er = ::MsiRecordSetStringW(hRec, i + 3, va_arg(args, WCHAR*));
74 ExitOnFailure(HRESULT_FROM_WIN32(er), "failed to set string string into error message");
75 }
76 va_end(args);
77
78 er = WcaProcessMessage(static_cast<INSTALLMESSAGE>(uiType), hRec);
79LExit:
80 if (args)
81 {
82 va_end(args);
83 }
84
85 if (hRec)
86 {
87 ::MsiCloseHandle(hRec);
88 }
89
90 return er;
91}
92
93
94/********************************************************************
95WcaProgressMessage() - extends the progress bar or sends a progress
96update from the CustomAction
97
98********************************************************************/
99extern "C" HRESULT WIXAPI WcaProgressMessage(
100 __in UINT uiCost,
101 __in BOOL fExtendProgressBar
102 )
103{
104 static BOOL fExplicitProgressMessages = FALSE;
105
106 HRESULT hr = S_OK;
107 UINT er = ERROR_SUCCESS;
108 MSIHANDLE hRec = ::MsiCreateRecord(3);
109
110 // if aren't extending the progress bar and we haven't switched into explicit message mode
111 if (!fExtendProgressBar && !fExplicitProgressMessages)
112 {
113 AssertSz(::MsiGetMode(WcaGetInstallHandle(), MSIRUNMODE_SCHEDULED) ||
114 ::MsiGetMode(WcaGetInstallHandle(), MSIRUNMODE_COMMIT) ||
115 ::MsiGetMode(WcaGetInstallHandle(), MSIRUNMODE_ROLLBACK), "can only send progress bar messages in a deferred CustomAction");
116
117 // tell Darwin to use explicit progress messages
118 ::MsiRecordSetInteger(hRec, 1, 1);
119 ::MsiRecordSetInteger(hRec, 2, 1);
120 ::MsiRecordSetInteger(hRec, 3, 0);
121
122 er = WcaProcessMessage(INSTALLMESSAGE_PROGRESS, hRec);
123 if (0 == er || IDOK == er || IDYES == er)
124 {
125 hr = S_OK;
126 }
127 else if (IDABORT == er || IDCANCEL == er)
128 {
129 WcaSetReturnValue(ERROR_INSTALL_USEREXIT); // note that the user said exit
130 ExitFunction1(hr = S_FALSE);
131 }
132 else
133 {
134 hr = E_UNEXPECTED;
135 }
136 ExitOnFailure(hr, "failed to tell Darwin to use explicit progress messages");
137
138 fExplicitProgressMessages = TRUE;
139 }
140#if DEBUG
141 else if (fExtendProgressBar) // if we are extending the progress bar, make sure we're not deferred
142 {
143 AssertSz(!::MsiGetMode(WcaGetInstallHandle(), MSIRUNMODE_SCHEDULED), "cannot add ticks to progress bar length from deferred CustomAction");
144 }
145#endif
146
147 // send the progress message
148 ::MsiRecordSetInteger(hRec, 1, (fExtendProgressBar) ? 3 : 2);
149 ::MsiRecordSetInteger(hRec, 2, uiCost);
150 ::MsiRecordSetInteger(hRec, 3, 0);
151
152 er = WcaProcessMessage(INSTALLMESSAGE_PROGRESS, hRec);
153 if (0 == er || IDOK == er || IDYES == er)
154 {
155 hr = S_OK;
156 }
157 else if (IDABORT == er || IDCANCEL == er)
158 {
159 WcaSetReturnValue(ERROR_INSTALL_USEREXIT); // note that the user said exit
160 hr = S_FALSE;
161 }
162 else
163 {
164 hr = E_UNEXPECTED;
165 }
166
167LExit:
168 if (hRec)
169 {
170 ::MsiCloseHandle(hRec);
171 }
172
173 return hr;
174}
175
176
177/********************************************************************
178WcaIsInstalling() - determines if a pair of installstates means install
179
180********************************************************************/
181extern "C" BOOL WIXAPI WcaIsInstalling(
182 __in INSTALLSTATE isInstalled,
183 __in INSTALLSTATE isAction
184 )
185{
186 return (INSTALLSTATE_LOCAL == isAction ||
187 INSTALLSTATE_SOURCE == isAction ||
188 (INSTALLSTATE_DEFAULT == isAction &&
189 (INSTALLSTATE_LOCAL == isInstalled ||
190 INSTALLSTATE_SOURCE == isInstalled)));
191}
192
193/********************************************************************
194WcaIsReInstalling() - determines if a pair of installstates means reinstall
195
196********************************************************************/
197extern "C" BOOL WIXAPI WcaIsReInstalling(
198 __in INSTALLSTATE isInstalled,
199 __in INSTALLSTATE isAction
200 )
201{
202 return ((INSTALLSTATE_LOCAL == isAction ||
203 INSTALLSTATE_SOURCE == isAction ||
204 INSTALLSTATE_DEFAULT == isAction) &&
205 (INSTALLSTATE_LOCAL == isInstalled ||
206 INSTALLSTATE_SOURCE == isInstalled));
207}
208
209
210/********************************************************************
211WcaIsUninstalling() - determines if a pair of installstates means uninstall
212
213********************************************************************/
214extern "C" BOOL WIXAPI WcaIsUninstalling(
215 __in INSTALLSTATE isInstalled,
216 __in INSTALLSTATE isAction
217 )
218{
219 return ((INSTALLSTATE_ABSENT == isAction ||
220 INSTALLSTATE_REMOVED == isAction) &&
221 (INSTALLSTATE_LOCAL == isInstalled ||
222 INSTALLSTATE_SOURCE == isInstalled));
223}
224
225
226/********************************************************************
227WcaGetComponentToDo() - gets a component's install states and
228determines if they mean install, uninstall, or reinstall.
229********************************************************************/
230extern "C" WCA_TODO WIXAPI WcaGetComponentToDo(
231 __in_z LPCWSTR wzComponentId
232 )
233{
234 INSTALLSTATE isInstalled = INSTALLSTATE_UNKNOWN;
235 INSTALLSTATE isAction = INSTALLSTATE_UNKNOWN;
236 if (ERROR_SUCCESS != ::MsiGetComponentStateW(WcaGetInstallHandle(), wzComponentId, &isInstalled, &isAction))
237 {
238 return WCA_TODO_UNKNOWN;
239 }
240
241 if (WcaIsReInstalling(isInstalled, isAction))
242 {
243 return WCA_TODO_REINSTALL;
244 }
245 else if (WcaIsUninstalling(isInstalled, isAction))
246 {
247 return WCA_TODO_UNINSTALL;
248 }
249 else if (WcaIsInstalling(isInstalled, isAction))
250 {
251 return WCA_TODO_INSTALL;
252 }
253 else
254 {
255 return WCA_TODO_UNKNOWN;
256 }
257}
258
259
260/********************************************************************
261WcaSetComponentState() - sets the install state of a Component
262
263********************************************************************/
264extern "C" HRESULT WIXAPI WcaSetComponentState(
265 __in_z LPCWSTR wzComponent,
266 __in INSTALLSTATE isState
267 )
268{
269 UINT er = ::MsiSetComponentStateW(WcaGetInstallHandle(), wzComponent, isState);
270 if (ERROR_INSTALL_USEREXIT == er)
271 {
272 WcaSetReturnValue(er);
273 }
274
275 return HRESULT_FROM_WIN32(er);
276}
277
278
279/********************************************************************
280WcaTableExists() - determines if installing database contains a table
281
282********************************************************************/
283extern "C" HRESULT WIXAPI WcaTableExists(
284 __in_z LPCWSTR wzTable
285 )
286{
287 HRESULT hr = S_OK;
288 UINT er = ERROR_SUCCESS;
289
290 // NOTE: The following line of commented out code should work in a
291 // CustomAction but does not in Windows Installer v1.1
292 // er = ::MsiDatabaseIsTablePersistentW(hDatabase, wzTable);
293
294 // a "most elegant" workaround a Darwin v1.1 bug
295 PMSIHANDLE hRec;
296 er = ::MsiDatabaseGetPrimaryKeysW(WcaGetDatabaseHandle(), wzTable, &hRec);
297
298 if (ERROR_SUCCESS == er)
299 {
300 hr = S_OK;
301 }
302 else if (ERROR_INVALID_TABLE == er)
303 {
304 hr = S_FALSE;
305 }
306 else
307 {
308 hr = E_FAIL;
309 }
310 Assert(SUCCEEDED(hr));
311
312 return hr;
313}
314
315
316/********************************************************************
317WcaOpenView() - opens a view on the installing database
318
319********************************************************************/
320extern "C" HRESULT WIXAPI WcaOpenView(
321 __in_z LPCWSTR wzSql,
322 __out MSIHANDLE* phView
323 )
324{
325 if (!wzSql || !*wzSql|| !phView)
326 {
327 return E_INVALIDARG;
328 }
329
330 HRESULT hr = S_OK;
331 UINT er = ::MsiDatabaseOpenViewW(WcaGetDatabaseHandle(), wzSql, phView);
332 ExitOnWin32Error(er, hr, "failed to open view on database with SQL: %ls", wzSql);
333
334LExit:
335 return hr;
336}
337
338
339/********************************************************************
340WcaExecuteView() - executes a parameterized open view on the installing database
341
342********************************************************************/
343extern "C" HRESULT WIXAPI WcaExecuteView(
344 __in MSIHANDLE hView,
345 __in MSIHANDLE hRec
346 )
347{
348 if (!hView)
349 {
350 return E_INVALIDARG;
351 }
352 AssertSz(hRec, "Use WcaOpenExecuteView() if you don't need to pass in a record");
353
354 HRESULT hr = S_OK;
355 UINT er = ::MsiViewExecute(hView, hRec);
356 ExitOnWin32Error(er, hr, "failed to execute view");
357
358LExit:
359 return hr;
360}
361
362
363/********************************************************************
364WcaOpenExecuteView() - opens and executes a view on the installing database
365
366********************************************************************/
367extern "C" HRESULT WIXAPI WcaOpenExecuteView(
368 __in_z LPCWSTR wzSql,
369 __out MSIHANDLE* phView
370 )
371{
372 if (!wzSql || !*wzSql|| !phView)
373 {
374 return E_INVALIDARG;
375 }
376
377 HRESULT hr = S_OK;
378 UINT er = ::MsiDatabaseOpenViewW(WcaGetDatabaseHandle(), wzSql, phView);
379 ExitOnWin32Error(er, hr, "failed to open view on database");
380
381 er = ::MsiViewExecute(*phView, NULL);
382 ExitOnWin32Error(er, hr, "failed to execute view");
383
384LExit:
385 return hr;
386}
387
388
389/********************************************************************
390WcaFetchRecord() - gets the next record from a view on the installing database
391
392********************************************************************/
393extern "C" HRESULT WIXAPI WcaFetchRecord(
394 __in MSIHANDLE hView,
395 __out MSIHANDLE* phRec
396 )
397{
398 if (!hView|| !phRec)
399 {
400 return E_INVALIDARG;
401 }
402
403 HRESULT hr = S_OK;
404 UINT er = ::MsiViewFetch(hView, phRec);
405 hr = HRESULT_FROM_WIN32(er);
406 if (FAILED(hr) && E_NOMOREITEMS != hr)
407 {
408 ExitOnFailure(hr, "failed to fetch record from view");
409 }
410
411LExit:
412 return hr;
413}
414
415
416/********************************************************************
417WcaFetchSingleRecord() - gets a single record from a view on the installing database
418
419********************************************************************/
420extern "C" HRESULT WIXAPI WcaFetchSingleRecord(
421 __in MSIHANDLE hView,
422 __out MSIHANDLE* phRec
423 )
424{
425 if (!hView|| !phRec)
426 {
427 return E_INVALIDARG;
428 }
429
430 HRESULT hr = S_OK;
431 UINT er = ::MsiViewFetch(hView, phRec);
432 if (ERROR_NO_MORE_ITEMS == er)
433 {
434 hr = S_FALSE;
435 }
436 else
437 {
438 hr = HRESULT_FROM_WIN32(er);
439 }
440 ExitOnFailure(hr, "failed to fetch single record from view");
441
442#ifdef DEBUG // only do this in debug to verify that a single record was returned
443 MSIHANDLE hRecTest;
444 er = ::MsiViewFetch(hView, &hRecTest);
445 AssertSz(ERROR_NO_MORE_ITEMS == er && NULL == hRecTest, "WcaSingleFetch() did not fetch a single record");
446 ::MsiCloseHandle(hRecTest);
447#endif
448
449LExit:
450 return hr;
451}
452
453
454/********************************************************************
455WcaGetProperty - gets a string property value from the active install
456
457********************************************************************/
458extern "C" HRESULT WIXAPI WcaGetProperty(
459 __in_z LPCWSTR wzProperty,
460 __inout LPWSTR* ppwzData
461 )
462{
463 if (!wzProperty || !*wzProperty || !ppwzData)
464 {
465 return E_INVALIDARG;
466 }
467
468 HRESULT hr = S_OK;
469 UINT er = ERROR_SUCCESS;
470 DWORD cch = 0;
471 SIZE_T cchMax = 0;
472
473 if (!*ppwzData)
474 {
475 WCHAR szEmpty[1] = L"";
476 er = ::MsiGetPropertyW(WcaGetInstallHandle(), wzProperty, szEmpty, &cch);
477 if (ERROR_MORE_DATA == er || ERROR_SUCCESS == er)
478 {
479 hr = StrAlloc(ppwzData, ++cch);
480 }
481 else
482 {
483 hr = HRESULT_FROM_WIN32(er);
484 }
485 ExitOnRootFailure(hr, "Failed to allocate string for Property '%ls'", wzProperty);
486 }
487 else
488 {
489 hr = StrMaxLength(*ppwzData, &cchMax);
490 ExitOnFailure(hr, "Failed to get previous size of property data string.");
491
492 cch = (DWORD)min(MAXDWORD, cchMax);
493 }
494
495 er = ::MsiGetPropertyW(WcaGetInstallHandle(), wzProperty, *ppwzData, &cch);
496 if (ERROR_MORE_DATA == er)
497 {
498 Assert(*ppwzData);
499 hr = StrAlloc(ppwzData, ++cch);
500 ExitOnFailure(hr, "Failed to allocate string for Property '%ls'", wzProperty);
501
502 er = ::MsiGetPropertyW(WcaGetInstallHandle(), wzProperty, *ppwzData, &cch);
503 }
504 ExitOnWin32Error(er, hr, "Failed to get data for property '%ls'", wzProperty);
505
506LExit:
507 return hr;
508}
509
510
511/********************************************************************
512WcaGetFormattedProperty - gets a formatted string property value from
513the active install
514
515********************************************************************/
516extern "C" HRESULT WIXAPI WcaGetFormattedProperty(
517 __in_z LPCWSTR wzProperty,
518 __out LPWSTR* ppwzData
519 )
520{
521 if (!wzProperty || !*wzProperty || !ppwzData)
522 {
523 return E_INVALIDARG;
524 }
525
526 HRESULT hr = S_OK;
527 LPWSTR pwzPropertyValue = NULL;
528
529 hr = WcaGetProperty(wzProperty, &pwzPropertyValue);
530 ExitOnFailure(hr, "failed to get %ls", wzProperty);
531
532 hr = WcaGetFormattedString(pwzPropertyValue, ppwzData);
533 ExitOnFailure(hr, "failed to get formatted value for property: '%ls' with value: '%ls'", wzProperty, pwzPropertyValue);
534
535LExit:
536 ReleaseStr(pwzPropertyValue);
537
538 return hr;
539}
540
541
542/********************************************************************
543WcaGetFormattedString - gets a formatted string value from
544the active install
545
546********************************************************************/
547extern "C" HRESULT WIXAPI WcaGetFormattedString(
548 __in_z LPCWSTR wzString,
549 __out LPWSTR* ppwzData
550 )
551{
552 if (!wzString || !*wzString || !ppwzData)
553 {
554 return E_INVALIDARG;
555 }
556
557 HRESULT hr = S_OK;
558 UINT er = ERROR_SUCCESS;
559 PMSIHANDLE hRecord = ::MsiCreateRecord(1);
560 DWORD cch = 0;
561 SIZE_T cchMax = 0;
562
563 er = ::MsiRecordSetStringW(hRecord, 0, wzString);
564 ExitOnWin32Error(er, hr, "Failed to set record field 0 with '%ls'", wzString);
565
566 if (!*ppwzData)
567 {
568 WCHAR szEmpty[1] = L"";
569 er = ::MsiFormatRecordW(WcaGetInstallHandle(), hRecord, szEmpty, &cch);
570 if (ERROR_MORE_DATA == er || ERROR_SUCCESS == er)
571 {
572 hr = StrAlloc(ppwzData, ++cch);
573 }
574 else
575 {
576 hr = HRESULT_FROM_WIN32(er);
577 }
578 ExitOnFailure(hr, "Failed to allocate string for formatted string: '%ls'", wzString);
579 }
580 else
581 {
582 hr = StrMaxLength(*ppwzData, &cchMax);
583 ExitOnFailure(hr, "Failed to get previous size of property data string");
584
585 cch = (DWORD)min(MAXDWORD, cchMax);
586 }
587
588 er = ::MsiFormatRecordW(WcaGetInstallHandle(), hRecord, *ppwzData, &cch);
589 if (ERROR_MORE_DATA == er)
590 {
591 hr = StrAlloc(ppwzData, ++cch);
592 ExitOnFailure(hr, "Failed to allocate string for formatted string: '%ls'", wzString);
593
594 er = ::MsiFormatRecordW(WcaGetInstallHandle(), hRecord, *ppwzData, &cch);
595 }
596 ExitOnWin32Error(er, hr, "Failed to get formatted string: '%ls'", wzString);
597
598LExit:
599 return hr;
600}
601
602
603/********************************************************************
604WcaGetIntProperty - gets an integer property value from the active install
605
606********************************************************************/
607extern "C" HRESULT WIXAPI WcaGetIntProperty(
608 __in_z LPCWSTR wzProperty,
609 __inout int* piData
610 )
611{
612 if (!piData)
613 return E_INVALIDARG;
614
615 HRESULT hr = S_OK;
616 UINT er;
617
618 WCHAR wzValue[32];
619 DWORD cch = countof(wzValue) - 1;
620
621 er = ::MsiGetPropertyW(WcaGetInstallHandle(), wzProperty, wzValue, &cch);
622 ExitOnWin32Error(er, hr, "Failed to get data for property '%ls'", wzProperty);
623
624 *piData = wcstol(wzValue, NULL, 10);
625
626LExit:
627 return hr;
628}
629
630
631/********************************************************************
632WcaGetTargetPath - gets the target path for a specified folder
633
634********************************************************************/
635extern "C" HRESULT WIXAPI WcaGetTargetPath(
636 __in_z LPCWSTR wzFolder,
637 __out LPWSTR* ppwzData
638 )
639{
640 if (!wzFolder || !*wzFolder || !ppwzData)
641 return E_INVALIDARG;
642
643 HRESULT hr = S_OK;
644
645 UINT er = ERROR_SUCCESS;
646 DWORD cch = 0;
647 SIZE_T cchMax = 0;
648
649 if (!*ppwzData)
650 {
651 WCHAR szEmpty[1] = L"";
652 er = ::MsiGetTargetPathW(WcaGetInstallHandle(), wzFolder, szEmpty, &cch);
653 if (ERROR_MORE_DATA == er || ERROR_SUCCESS == er)
654 {
655 ++cch; //Add one for the null terminator
656 hr = StrAlloc(ppwzData, cch);
657 }
658 else
659 {
660 hr = HRESULT_FROM_WIN32(er);
661 }
662 ExitOnFailure(hr, "Failed to allocate string for target path of folder: '%ls'", wzFolder);
663 }
664 else
665 {
666 hr = StrMaxLength(*ppwzData, &cchMax);
667 ExitOnFailure(hr, "Failed to get previous size of string");
668
669 cch = (DWORD)min(MAXDWORD, cchMax);
670 }
671
672 er = ::MsiGetTargetPathW(WcaGetInstallHandle(), wzFolder, *ppwzData, &cch);
673 if (ERROR_MORE_DATA == er)
674 {
675 ++cch;
676 hr = StrAlloc(ppwzData, cch);
677 ExitOnFailure(hr, "Failed to allocate string for target path of folder: '%ls'", wzFolder);
678
679 er = ::MsiGetTargetPathW(WcaGetInstallHandle(), wzFolder, *ppwzData, &cch);
680 }
681 ExitOnWin32Error(er, hr, "Failed to get target path for folder '%ls'", wzFolder);
682
683LExit:
684 return hr;
685}
686
687
688/********************************************************************
689WcaSetProperty - sets a string property value in the active install
690
691********************************************************************/
692extern "C" HRESULT WIXAPI WcaSetProperty(
693 __in_z LPCWSTR wzPropertyName,
694 __in_z LPCWSTR wzPropertyValue
695 )
696{
697 HRESULT hr = S_OK;
698
699 if (!wzPropertyName || !*wzPropertyName || !wzPropertyValue)
700 return E_INVALIDARG;
701
702 UINT er = ::MsiSetPropertyW(WcaGetInstallHandle(), wzPropertyName, wzPropertyValue);
703 ExitOnWin32Error(er, hr, "failed to set property: %ls", wzPropertyName);
704
705LExit:
706 return hr;
707}
708
709
710/********************************************************************
711WcaSetIntProperty - sets a integer property value in the active install
712
713********************************************************************/
714extern "C" HRESULT WIXAPI WcaSetIntProperty(
715 __in_z LPCWSTR wzPropertyName,
716 __in int nPropertyValue
717 )
718{
719 if (!wzPropertyName || !*wzPropertyName)
720 return E_INVALIDARG;
721
722 // 12 characters should be enough for a 32-bit int: 10 digits, 1 sign, 1 null
723 WCHAR wzPropertyValue[13];
724 HRESULT hr = StringCchPrintfW(wzPropertyValue, countof(wzPropertyValue), L"%d", nPropertyValue);
725 ExitOnFailure(hr, "failed to convert into string property value: %d", nPropertyValue);
726
727 UINT er = ::MsiSetPropertyW(WcaGetInstallHandle(), wzPropertyName, wzPropertyValue);
728 ExitOnWin32Error(er, hr, "failed to set property: %ls", wzPropertyName);
729
730LExit:
731 return hr;
732}
733
734
735/********************************************************************
736WcaIsPropertySet() - returns TRUE if property is set
737
738********************************************************************/
739extern "C" BOOL WIXAPI WcaIsPropertySet(
740 __in LPCSTR szProperty
741 )
742{
743 DWORD cchProperty = 0;
744 char szEmpty[1] = "";
745#ifdef DEBUG
746 UINT er =
747#endif
748 ::MsiGetPropertyA(WcaGetInstallHandle(), szProperty, szEmpty, &cchProperty);
749 AssertSz(ERROR_INVALID_PARAMETER != er && ERROR_INVALID_HANDLE != er, "Unexpected return value from ::MsiGetProperty()");
750
751 return 0 < cchProperty; // property is set if the length is greater than zero
752}
753
754
755/********************************************************************
756WcaIsUnicodePropertySet() - returns TRUE if property is set
757
758********************************************************************/
759extern "C" BOOL WIXAPI WcaIsUnicodePropertySet(
760 __in LPCWSTR wzProperty
761 )
762{
763 DWORD cchProperty = 0;
764 wchar_t wzEmpty[1] = L"";
765#ifdef DEBUG
766 UINT er =
767#endif
768 ::MsiGetPropertyW(WcaGetInstallHandle(), wzProperty, wzEmpty, &cchProperty);
769 AssertSz(ERROR_INVALID_PARAMETER != er && ERROR_INVALID_HANDLE != er, "Unexpected return value from ::MsiGetProperty()");
770
771 return 0 < cchProperty; // property is set if the length is greater than zero
772}
773
774
775/********************************************************************
776WcaGetRecordInteger() - gets an integer field out of a record
777
778NOTE: returns S_FALSE if the field was null
779********************************************************************/
780extern "C" HRESULT WIXAPI WcaGetRecordInteger(
781 __in MSIHANDLE hRec,
782 __in UINT uiField,
783 __inout int* piData
784 )
785{
786 if (!hRec || !piData)
787 return E_INVALIDARG;
788
789 HRESULT hr = S_OK;
790 *piData = ::MsiRecordGetInteger(hRec, uiField);
791 if (MSI_NULL_INTEGER == *piData)
792 hr = S_FALSE;
793
794 //LExit:
795 return hr;
796}
797
798
799/********************************************************************
800WcaGetRecordString() - gets a string field out of a record
801
802********************************************************************/
803extern "C" HRESULT WIXAPI WcaGetRecordString(
804 __in MSIHANDLE hRec,
805 __in UINT uiField,
806 __inout LPWSTR* ppwzData
807 )
808{
809 if (!hRec || !ppwzData)
810 return E_INVALIDARG;
811
812 HRESULT hr = S_OK;
813 UINT er;
814 DWORD cch = 0;
815 SIZE_T cchMax = 0;
816
817 if (!*ppwzData)
818 {
819 WCHAR szEmpty[1] = L"";
820 er = ::MsiRecordGetStringW(hRec, uiField, szEmpty, &cch);
821 if (ERROR_MORE_DATA == er || ERROR_SUCCESS == er)
822 {
823 hr = StrAlloc(ppwzData, ++cch);
824 }
825 else
826 {
827 hr = HRESULT_FROM_WIN32(er);
828 }
829 ExitOnFailure(hr, "Failed to allocate memory for record string");
830 }
831 else
832 {
833 hr = StrMaxLength(*ppwzData, &cchMax);
834 ExitOnFailure(hr, "Failed to get previous size of string");
835
836 cch = (DWORD)min(MAXDWORD, cchMax);
837 }
838
839 er = ::MsiRecordGetStringW(hRec, uiField, *ppwzData, &cch);
840 if (ERROR_MORE_DATA == er)
841 {
842 hr = StrAlloc(ppwzData, ++cch);
843 ExitOnFailure(hr, "Failed to allocate memory for record string");
844
845 er = ::MsiRecordGetStringW(hRec, uiField, *ppwzData, &cch);
846 }
847 ExitOnWin32Error(er, hr, "Failed to get string from record");
848
849LExit:
850 return hr;
851}
852
853
854/********************************************************************
855HideNulls() - internal helper function to escape [~] in formatted strings
856
857********************************************************************/
858static void HideNulls(
859 __inout_z LPWSTR wzData
860 )
861{
862 LPWSTR pwz = wzData;
863
864 while(*pwz)
865 {
866 if (pwz[0] == L'[' && pwz[1] == L'~' && pwz[2] == L']') // found a null [~]
867 {
868 pwz[0] = L'!'; // turn it into !$!
869 pwz[1] = L'$';
870 pwz[2] = L'!';
871 pwz += 3;
872 }
873 else
874 {
875 ++pwz;
876 }
877 }
878}
879
880
881/********************************************************************
882RevealNulls() - internal helper function to unescape !$! in formatted strings
883
884********************************************************************/
885static void RevealNulls(
886 __inout_z LPWSTR wzData
887 )
888{
889 LPWSTR pwz = wzData;
890
891 while(*pwz)
892 {
893 if (pwz[0] == L'!' && pwz[1] == L'$' && pwz[2] == L'!') // found the fake null !$!
894 {
895 pwz[0] = L'['; // turn it back into [~]
896 pwz[1] = L'~';
897 pwz[2] = L']';
898 pwz += 3;
899 }
900 else
901 {
902 ++pwz;
903 }
904 }
905}
906
907
908/********************************************************************
909WcaGetRecordFormattedString() - gets formatted string filed from record
910
911********************************************************************/
912extern "C" HRESULT WIXAPI WcaGetRecordFormattedString(
913 __in MSIHANDLE hRec,
914 __in UINT uiField,
915 __inout LPWSTR* ppwzData
916 )
917{
918 if (!hRec || !ppwzData)
919 {
920 return E_INVALIDARG;
921 }
922
923 HRESULT hr = S_OK;
924 UINT er;
925 DWORD cch = 0;
926 SIZE_T cchMax = 0;
927 PMSIHANDLE hRecFormat;
928
929 // get the format string
930 hr = WcaGetRecordString(hRec, uiField, ppwzData);
931 ExitOnFailure(hr, "failed to get string from record");
932
933 if (!**ppwzData)
934 {
935 ExitFunction();
936 }
937
938 // hide the nulls '[~]' so we can get them back after formatting
939 HideNulls(*ppwzData);
940
941 // set up the format record
942 hRecFormat = ::MsiCreateRecord(1);
943 ExitOnNull(hRecFormat, hr, E_UNEXPECTED, "Failed to create record to format string");
944 hr = WcaSetRecordString(hRecFormat, 0, *ppwzData);
945 ExitOnFailure(hr, "failed to set string to format record");
946
947 // format the string
948 hr = StrMaxLength(*ppwzData, &cchMax);
949 ExitOnFailure(hr, "failed to get max length of string");
950
951 cch = (DWORD)min(MAXDWORD, cchMax);
952
953 er = ::MsiFormatRecordW(WcaGetInstallHandle(), hRecFormat, *ppwzData, &cch);
954 if (ERROR_MORE_DATA == er)
955 {
956 hr = StrAlloc(ppwzData, ++cch);
957 ExitOnFailure(hr, "Failed to allocate memory for record string");
958
959 er = ::MsiFormatRecordW(WcaGetInstallHandle(), hRecFormat, *ppwzData, &cch);
960 }
961 ExitOnWin32Error(er, hr, "Failed to format string");
962
963 // put the nulls back
964 RevealNulls(*ppwzData);
965
966LExit:
967 return hr;
968}
969
970
971/********************************************************************
972WcaGetRecordFormattedInteger() - gets formatted integer from record
973
974********************************************************************/
975extern "C" HRESULT WIXAPI WcaGetRecordFormattedInteger(
976 __in MSIHANDLE hRec,
977 __in UINT uiField,
978 __out int* piData
979 )
980{
981 if (!hRec || !piData)
982 {
983 return E_INVALIDARG;
984 }
985
986 HRESULT hr = S_OK;
987 LPWSTR pwzData = NULL;
988
989 hr = WcaGetRecordFormattedString(hRec, uiField, &pwzData);
990 ExitOnFailure(hr, "failed to get record field: %u", uiField);
991 if (pwzData && *pwzData)
992 {
993 LPWSTR wz = NULL;
994 *piData = wcstol(pwzData, &wz, 10);
995 if (wz && *wz)
996 {
997 hr = E_INVALIDARG;
998 ExitOnFailure(hr, "failed to parse record field: %u as number: %ls", uiField, pwzData);
999 }
1000 }
1001 else
1002 {
1003 *piData = MSI_NULL_INTEGER;
1004 }
1005
1006LExit:
1007 return hr;
1008}
1009
1010
1011/********************************************************************
1012WcaAllocStream() - creates a byte stream of the specified size
1013
1014NOTE: Use WcaFreeStream() to release the byte stream
1015********************************************************************/
1016extern "C" HRESULT WIXAPI WcaAllocStream(
1017 __deref_out_bcount_part(cbData, 0) BYTE** ppbData,
1018 __in SIZE_T cbData
1019 )
1020{
1021 Assert(ppbData);
1022 HRESULT hr;
1023 BYTE* pbNewData;
1024
1025 if (*ppbData)
1026 pbNewData = static_cast<BYTE*>(MemReAlloc(*ppbData, cbData, TRUE));
1027 else
1028 pbNewData = static_cast<BYTE*>(MemAlloc(cbData, TRUE));
1029
1030 if (!pbNewData)
1031 {
1032 ExitOnLastError(hr, "Failed to allocate string");
1033 }
1034
1035 *ppbData = pbNewData;
1036 pbNewData = NULL;
1037
1038 hr = S_OK;
1039LExit:
1040 ReleaseMem(pbNewData);
1041
1042 return hr;
1043}
1044
1045
1046/********************************************************************
1047WcaFreeStream() - frees a byte stream
1048
1049********************************************************************/
1050extern "C" HRESULT WIXAPI WcaFreeStream(
1051 __in BYTE* pbData
1052 )
1053{
1054 if (!pbData)
1055 return E_INVALIDARG;
1056
1057 HRESULT hr = MemFree(pbData);
1058 return hr;
1059}
1060
1061
1062/********************************************************************
1063WcaGetRecordStream() - gets a byte stream field from record
1064
1065********************************************************************/
1066extern "C" HRESULT WIXAPI WcaGetRecordStream(
1067 __in MSIHANDLE hRecBinary,
1068 __in UINT uiField,
1069 __deref_out_bcount_full(*pcbData) BYTE** ppbData,
1070 __out DWORD* pcbData
1071 )
1072{
1073 HRESULT hr = S_OK;
1074 UINT er = ERROR_SUCCESS;
1075
1076 if (!hRecBinary || !ppbData || !pcbData)
1077 return E_INVALIDARG;
1078
1079 *pcbData = 0;
1080 er = ::MsiRecordReadStream(hRecBinary, uiField, NULL, pcbData);
1081 ExitOnWin32Error(er, hr, "failed to get size of stream");
1082
1083 hr = WcaAllocStream(ppbData, *pcbData);
1084 ExitOnFailure(hr, "failed to allocate data for stream");
1085
1086 er = ::MsiRecordReadStream(hRecBinary, uiField, (char*)*ppbData, pcbData);
1087 ExitOnWin32Error(er, hr, "failed to read from stream");
1088
1089LExit:
1090 return hr;
1091}
1092
1093
1094/********************************************************************
1095WcaSetRecordString() - set a string field in record
1096
1097********************************************************************/
1098extern "C" HRESULT WIXAPI WcaSetRecordString(
1099 __in MSIHANDLE hRec,
1100 __in UINT uiField,
1101 __in_z LPCWSTR wzData
1102 )
1103{
1104 if (!hRec || !wzData)
1105 return E_INVALIDARG;
1106
1107 HRESULT hr = S_OK;
1108 UINT er = ::MsiRecordSetStringW(hRec, uiField, wzData);
1109 ExitOnWin32Error(er, hr, "failed to set string in record");
1110
1111LExit:
1112 return hr;
1113}
1114
1115
1116/********************************************************************
1117WcaSetRecordInteger() - set a integer field in record
1118
1119********************************************************************/
1120extern "C" HRESULT WIXAPI WcaSetRecordInteger(
1121 __in MSIHANDLE hRec,
1122 __in UINT uiField,
1123 __in int iValue
1124 )
1125{
1126 if (!hRec)
1127 return E_INVALIDARG;
1128
1129 HRESULT hr = S_OK;
1130 UINT er = ::MsiRecordSetInteger(hRec, uiField, iValue);
1131 ExitOnWin32Error(er, hr, "failed to set integer in record");
1132
1133LExit:
1134 return hr;
1135}
1136
1137
1138/********************************************************************
1139
1140WcaDoDeferredAction() - schedules an action at this point in the script
1141
1142********************************************************************/
1143extern "C" HRESULT WIXAPI WcaDoDeferredAction(
1144 __in_z LPCWSTR wzAction,
1145 __in_z LPCWSTR wzCustomActionData,
1146 __in UINT uiCost
1147 )
1148{
1149 HRESULT hr = S_OK;
1150 UINT er;
1151
1152 if (wzCustomActionData && *wzCustomActionData)
1153 {
1154 er = ::MsiSetPropertyW(WcaGetInstallHandle(), wzAction, wzCustomActionData);
1155 ExitOnWin32Error(er, hr, "Failed to set CustomActionData for deferred action");
1156 }
1157
1158 if (0 < uiCost)
1159 {
1160 hr = WcaProgressMessage(uiCost, TRUE); // add ticks to the progress bar
1161 // TODO: handle the return codes correctly
1162 }
1163
1164 er = ::MsiDoActionW(WcaGetInstallHandle(), wzAction);
1165 if (ERROR_INSTALL_USEREXIT == er)
1166 {
1167 WcaSetReturnValue(er);
1168 }
1169 ExitOnWin32Error(er, hr, "Failed MsiDoAction on deferred action");
1170
1171LExit:
1172 return hr;
1173}
1174
1175
1176/********************************************************************
1177WcaCountOfCustomActionDataRecords() - counts the number of records
1178passed to a deferred CustomAction
1179
1180********************************************************************/
1181extern "C" DWORD WIXAPI WcaCountOfCustomActionDataRecords(
1182 __in_z LPCWSTR wzData
1183 )
1184{
1185 WCHAR delim[] = {MAGIC_MULTISZ_DELIM, 0}; // magic char followed by NULL terminator
1186 DWORD dwCount = 0;
1187
1188 // Loop through until there are no delimiters, we are at the end of the string, or the delimiter is the last character in the string
1189 for (LPCWSTR pwzCurrent = wzData; pwzCurrent && *pwzCurrent && *(pwzCurrent + 1); pwzCurrent = wcsstr(pwzCurrent, delim))
1190 {
1191 ++dwCount;
1192 ++pwzCurrent;
1193 }
1194
1195 return dwCount;
1196}
1197
1198
1199/********************************************************************
1200BreakDownCustomActionData() - internal helper to chop up CustomActionData
1201
1202NOTE: this modifies the passed in data
1203********************************************************************/
1204static LPWSTR BreakDownCustomActionData(
1205 __inout LPWSTR* ppwzData
1206 )
1207{
1208 if (!ppwzData)
1209 return NULL;
1210 if (0 == *ppwzData)
1211 return NULL;
1212
1213 WCHAR delim[] = {MAGIC_MULTISZ_DELIM, 0}; // magic char followed by Null terminator
1214
1215 LPWSTR pwzReturn = *ppwzData;
1216 LPWSTR pwz = wcsstr(pwzReturn, delim);
1217 if (pwz)
1218 {
1219 *pwz = 0;
1220 *ppwzData = pwz + 1;
1221 }
1222 else
1223 *ppwzData = 0;
1224
1225 return pwzReturn;
1226}
1227
1228
1229/********************************************************************
1230RevertCustomActionData() - Reverts custom action data changes made
1231 by BreakDownCustomActionData;
1232
1233NOTE: this modifies the passed in data
1234********************************************************************/
1235extern "C" void WIXAPI RevertCustomActionData(
1236 __in LPWSTR wzRevertTo,
1237 __in LPCWSTR wzRevertFrom
1238 )
1239{
1240 if (!wzRevertTo)
1241 return;
1242 if (!wzRevertFrom)
1243 return;
1244 // start at the revert point and replace all \0 with MAGIC_MULTISZ_DELIM
1245 for(LPWSTR wzIndex = wzRevertTo; wzIndex < wzRevertFrom; wzIndex++)
1246 {
1247 if (0 == *wzIndex)
1248 {
1249 *wzIndex = MAGIC_MULTISZ_DELIM;
1250 }
1251 }
1252 return;
1253}
1254
1255/********************************************************************
1256WcaReadStringFromCaData() - reads a string out of the CustomActionData
1257
1258NOTE: this modifies the passed in ppwzCustomActionData variable
1259********************************************************************/
1260extern "C" HRESULT WIXAPI WcaReadStringFromCaData(
1261 __deref_in LPWSTR* ppwzCustomActionData,
1262 __deref_out_z LPWSTR* ppwzString
1263 )
1264{
1265 HRESULT hr = S_OK;
1266
1267 LPCWSTR pwz = BreakDownCustomActionData(ppwzCustomActionData);
1268 if (!pwz)
1269 return E_NOMOREITEMS;
1270
1271 hr = StrAllocString(ppwzString, pwz, 0);
1272 ExitOnFailure(hr, "failed to allocate memory for string");
1273
1274 hr = S_OK;
1275LExit:
1276 return hr;
1277}
1278
1279
1280/********************************************************************
1281WcaReadIntegerFromCaData() - reads an integer out of the CustomActionData
1282
1283NOTE: this modifies the passed in ppwzCustomActionData variable
1284********************************************************************/
1285extern "C" HRESULT WIXAPI WcaReadIntegerFromCaData(
1286 __deref_in LPWSTR* ppwzCustomActionData,
1287 __out int* piResult
1288 )
1289{
1290 LPCWSTR pwz = BreakDownCustomActionData(ppwzCustomActionData);
1291 if (!pwz || !*pwz)
1292 return E_NOMOREITEMS;
1293
1294 *piResult = wcstol(pwz, NULL, 10);
1295 return S_OK;
1296}
1297
1298
1299/********************************************************************
1300WcaReadStreamFromCaData() - reads a stream out of the CustomActionData
1301
1302NOTE: this modifies the passed in ppwzCustomActionData variable
1303NOTE: returned stream should be freed with WcaFreeStream()
1304********************************************************************/
1305extern "C" HRESULT WIXAPI WcaReadStreamFromCaData(
1306 __deref_in LPWSTR* ppwzCustomActionData,
1307 __deref_out_bcount(*pcbData) BYTE** ppbData,
1308 __out DWORD_PTR* pcbData
1309 )
1310{
1311 HRESULT hr;
1312
1313 LPCWSTR pwz = BreakDownCustomActionData(ppwzCustomActionData);
1314 if (!pwz)
1315 return E_NOMOREITEMS;
1316
1317 hr = StrAllocBase85Decode(pwz, ppbData, pcbData);
1318 ExitOnFailure(hr, "failed to decode string into stream");
1319
1320LExit:
1321 return hr;
1322}
1323
1324
1325/********************************************************************
1326WcaWriteStringToCaData() - adds a string to the CustomActionData to
1327feed a deferred CustomAction
1328
1329********************************************************************/
1330extern "C" HRESULT WIXAPI WcaWriteStringToCaData(
1331 __in_z LPCWSTR wzString,
1332 __deref_inout_z_opt LPWSTR* ppwzCustomActionData
1333 )
1334{
1335 HRESULT hr = S_OK;
1336 WCHAR delim[] = {MAGIC_MULTISZ_DELIM, 0}; // magic char followed by NULL terminator
1337 SIZE_T cchString = 0;
1338 SIZE_T cchCustomActionData = 0;
1339 SIZE_T cchMax = 0;
1340
1341 if (!ppwzCustomActionData)
1342 {
1343 ExitFunction1(hr = E_INVALIDARG);
1344 }
1345
1346 hr = ::StringCchLengthW(wzString, STRSAFE_MAX_LENGTH, reinterpret_cast<size_t*>(&cchString));
1347 ExitOnRootFailure(hr, "failed to get length of ca data string");
1348
1349 ++cchString; // assume we'll be adding the delim
1350
1351 if (*ppwzCustomActionData)
1352 {
1353 hr = StrMaxLength(*ppwzCustomActionData, &cchCustomActionData);
1354 ExitOnFailure(hr, "failed to get max length of custom action data");
1355
1356 hr = ::StringCchLengthW(*ppwzCustomActionData, STRSAFE_MAX_LENGTH, reinterpret_cast<size_t*>(&cchMax));
1357 ExitOnRootFailure(hr, "failed to get length of custom action data");
1358 }
1359
1360 if ((cchCustomActionData - cchMax) < cchString + 1)
1361 {
1362 cchCustomActionData += cchString + 1 + 255; // add 255 for good measure
1363 cchCustomActionData = min(STRSAFE_MAX_LENGTH, cchCustomActionData);
1364
1365 hr = StrAlloc(ppwzCustomActionData, cchCustomActionData);
1366 ExitOnFailure(hr, "Failed to allocate memory for CustomActionData string");
1367 }
1368
1369 if (**ppwzCustomActionData) // if data exists toss the delimiter on before adding more to the end
1370 {
1371 hr = ::StringCchCatW(*ppwzCustomActionData, cchCustomActionData, delim);
1372 ExitOnRootFailure(hr, "Failed to concatenate CustomActionData string");
1373 }
1374
1375 hr = ::StringCchCatW(*ppwzCustomActionData, cchCustomActionData, wzString);
1376 ExitOnRootFailure(hr, "Failed to concatenate CustomActionData string");
1377
1378LExit:
1379 return hr;
1380}
1381
1382
1383/********************************************************************
1384WcaWriteIntegerToCaData() - adds an integer to the CustomActionData to
1385feed a deferred CustomAction
1386
1387********************************************************************/
1388extern "C" HRESULT WIXAPI WcaWriteIntegerToCaData(
1389 __in int i,
1390 __deref_out_z_opt LPWSTR* ppwzCustomActionData
1391 )
1392{
1393 WCHAR wzBuffer[13];
1394 StringCchPrintfW(wzBuffer, countof(wzBuffer), L"%d", i);
1395
1396 return WcaWriteStringToCaData(wzBuffer, ppwzCustomActionData);
1397}
1398
1399
1400/********************************************************************
1401WcaWriteStreamToCaData() - adds a byte stream to the CustomActionData to
1402feed a deferred CustomAction
1403
1404********************************************************************/
1405extern "C" HRESULT WIXAPI WcaWriteStreamToCaData(
1406 __in_bcount(cbData) const BYTE* pbData,
1407 __in SIZE_T cbData,
1408 __deref_inout_z_opt LPWSTR* ppwzCustomActionData
1409 )
1410{
1411 HRESULT hr;
1412 LPWSTR pwzData = NULL;
1413
1414 hr = StrAllocBase85Encode(pbData, cbData, &pwzData);
1415 ExitOnFailure(hr, "failed to encode data into string");
1416
1417 hr = WcaWriteStringToCaData(pwzData, ppwzCustomActionData);
1418
1419LExit:
1420 ReleaseStr(pwzData);
1421 return hr;
1422}
1423
1424
1425/********************************************************************
1426WcaAddTempRecord - adds a temporary record to the active database
1427
1428NOTE: you cannot use PMSIHANDLEs for the __in/__out parameters
1429NOTE: uiUniquifyColumn can be 0 if no column needs to be made unique
1430********************************************************************/
1431extern "C" HRESULT __cdecl WcaAddTempRecord(
1432 __inout MSIHANDLE* phTableView,
1433 __inout MSIHANDLE* phColumns,
1434 __in_z LPCWSTR wzTable,
1435 __out_opt MSIDBERROR* pdbError,
1436 __in UINT uiUniquifyColumn,
1437 __in UINT cColumns,
1438 ...
1439 )
1440{
1441 Assert(phTableView && phColumns);
1442
1443 static DWORD dwUniquifyValue = ::GetTickCount();
1444
1445 HRESULT hr = S_OK;
1446 UINT er = ERROR_SUCCESS;
1447
1448 LPWSTR pwzQuery = NULL;
1449 PMSIHANDLE hTempRec;
1450 DWORD i;
1451 va_list args;
1452
1453 LPWSTR pwzData = NULL;
1454 LPWSTR pwzUniquify = NULL;
1455
1456 //
1457 // if we don't have a table and its columns already
1458 //
1459 if (NULL == *phTableView)
1460 {
1461 // set the query
1462 hr = StrAllocFormatted(&pwzQuery, L"SELECT * FROM `%s`",wzTable);
1463 ExitOnFailure(hr, "failed to allocate string for query");
1464
1465 // Open and Execute the temp View
1466 hr = WcaOpenExecuteView(pwzQuery, phTableView);
1467 ExitOnFailure(hr, "failed to openexecute temp view with query %ls", pwzQuery);
1468 }
1469
1470 if (NULL == *phColumns)
1471 {
1472 // use GetColumnInfo to populate the datatype record
1473 er = ::MsiViewGetColumnInfo(*phTableView, MSICOLINFO_TYPES, phColumns);
1474 ExitOnWin32Error(er, hr, "failed to columns for table: %ls", wzTable);
1475 }
1476 AssertSz(::MsiRecordGetFieldCount(*phColumns) == cColumns, "passed in argument does not match number of columns in table");
1477
1478 //
1479 // create the temp record
1480 //
1481 hTempRec = ::MsiCreateRecord(cColumns);
1482 ExitOnNull(hTempRec, hr, E_UNEXPECTED, "could not create temp record for table: %ls", wzTable);
1483
1484 //
1485 // loop through all the columns filling in the data
1486 //
1487 va_start(args, cColumns);
1488 for (i = 1; i <= cColumns; i++)
1489 {
1490 hr = WcaGetRecordString(*phColumns, i, &pwzData);
1491 ExitOnFailure(hr, "failed to get the data type for %d", i);
1492
1493 // if data type is string write string
1494 if (L's' == *pwzData || L'S' == *pwzData || L'g' == *pwzData || L'G' == *pwzData || L'l' == *pwzData || L'L' == *pwzData)
1495 {
1496 LPCWSTR wz = va_arg(args, WCHAR*);
1497
1498 // if this is the column that is supposed to be unique add the time stamp on the end
1499 if (uiUniquifyColumn == i)
1500 {
1501 hr = StrAllocFormatted(&pwzUniquify, L"%s%u", wz, ++dwUniquifyValue); // up the count so we have no collisions on the unique name
1502 ExitOnFailure(hr, "failed to allocate string for unique column: %d", uiUniquifyColumn);
1503
1504 wz = pwzUniquify;
1505 }
1506
1507 er = ::MsiRecordSetStringW(hTempRec, i, wz);
1508 ExitOnWin32Error(er, hr, "failed to set string value at position %d", i);
1509 }
1510 // if data type is integer write integer
1511 else if (L'i' == *pwzData || L'I' == *pwzData || L'j' == *pwzData || L'J' == *pwzData)
1512 {
1513 AssertSz(uiUniquifyColumn != i, "Cannot uniquify an integer column");
1514 int iData = va_arg(args, int);
1515
1516 er = ::MsiRecordSetInteger(hTempRec, i, iData);
1517 ExitOnWin32Error(er, hr, "failed to set integer value at position %d", i);
1518 }
1519 else
1520 {
1521 // not supporting binary streams so error out
1522 hr = HRESULT_FROM_WIN32(ERROR_DATATYPE_MISMATCH);
1523 ExitOnRootFailure(hr, "unsupported data type '%ls' in column: %d", pwzData, i);
1524 }
1525 }
1526 va_end(args);
1527
1528 //
1529 // add the temporary record to the MSI
1530 //
1531 er = ::MsiViewModify(*phTableView, MSIMODIFY_INSERT_TEMPORARY, hTempRec);
1532 hr = HRESULT_FROM_WIN32(er);
1533 if (FAILED(hr))
1534 {
1535 if (pdbError)
1536 {
1537 // MSI provides only a generic ERROR_FUNCTION_FAILED if a temporary row
1538 // can't be inserted; if we're being asked to provide the detailed error,
1539 // get it using the MSIMODIFY_VALIDATE_NEW flag
1540 er = ::MsiViewModify(*phTableView, MSIMODIFY_VALIDATE_NEW, hTempRec);
1541 hr = HRESULT_FROM_WIN32(er);
1542 }
1543
1544 WCHAR wzBuf[MAX_PATH];
1545 DWORD cchBuf = countof(wzBuf);
1546 MSIDBERROR dbErr = ::MsiViewGetErrorW(*phTableView, wzBuf, &cchBuf);
1547 if (pdbError)
1548 {
1549 *pdbError = dbErr;
1550 }
1551 ExitOnFailure(hr, "failed to add temporary row, dberr: %d, err: %ls", dbErr, wzBuf);
1552 }
1553
1554LExit:
1555 ReleaseStr(pwzUniquify);
1556 ReleaseStr(pwzData);
1557 ReleaseStr(pwzQuery);
1558
1559 return hr;
1560}
1561
1562
1563/********************************************************************
1564WcaDumpTable - dumps a table to the log file
1565
1566********************************************************************/
1567extern "C" HRESULT WIXAPI WcaDumpTable(
1568 __in_z LPCWSTR wzTable
1569 )
1570{
1571 HRESULT hr = S_OK;
1572 UINT er = ERROR_SUCCESS;
1573
1574 LPWSTR pwzQuery = NULL;
1575 PMSIHANDLE hView;
1576 PMSIHANDLE hColumns;
1577 DWORD cColumns = 0;
1578 PMSIHANDLE hRec;
1579
1580 LPWSTR pwzData = NULL;
1581 LPWSTR pwzPrint = NULL;
1582
1583 hr = StrAllocFormatted(&pwzQuery, L"SELECT * FROM `%s`",wzTable);
1584 ExitOnFailure(hr, "failed to allocate string for query");
1585
1586 // Open and Execute the temp View
1587 hr = WcaOpenExecuteView(pwzQuery, &hView);
1588 ExitOnFailure(hr, "failed to openexecute temp view with query %ls", pwzQuery);
1589
1590 // Use GetColumnInfo to populate the names of the columns.
1591 er = ::MsiViewGetColumnInfo(hView, MSICOLINFO_NAMES, &hColumns);
1592 hr = HRESULT_FROM_WIN32(er);
1593 ExitOnFailure(hr, "failed to get column info for table: %ls", wzTable);
1594
1595 cColumns = ::MsiRecordGetFieldCount(hColumns);
1596
1597 WcaLog(LOGMSG_STANDARD, "--- Begin Table Dump %ls ---", wzTable);
1598
1599 // Loop through all the columns filling in the data.
1600 for (DWORD i = 1; i <= cColumns; i++)
1601 {
1602 hr = WcaGetRecordString(hColumns, i, &pwzData);
1603 ExitOnFailure(hr, "failed to get the column name for %d", i);
1604
1605 hr = StrAllocConcat(&pwzPrint, pwzData, 0);
1606 ExitOnFailure(hr, "Failed to add column name.");
1607
1608 hr = StrAllocConcat(&pwzPrint, L"\t", 1);
1609 ExitOnFailure(hr, "Failed to add column name.");
1610 }
1611
1612 WcaLog(LOGMSG_STANDARD, "%ls", pwzPrint);
1613
1614 // Now dump the actual rows.
1615 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
1616 {
1617 if (pwzPrint && *pwzPrint)
1618 {
1619 *pwzPrint = L'\0';
1620 }
1621
1622 for (DWORD i = 1; i <= cColumns; i++)
1623 {
1624 hr = WcaGetRecordString(hRec, i, &pwzData);
1625 ExitOnFailure(hr, "failed to get the column name for %d", i);
1626
1627 hr = StrAllocConcat(&pwzPrint, pwzData, 0);
1628 ExitOnFailure(hr, "Failed to add column name.");
1629
1630 hr = StrAllocConcat(&pwzPrint, L"\t", 1);
1631 ExitOnFailure(hr, "Failed to add column name.");
1632 }
1633
1634 WcaLog(LOGMSG_STANDARD, "%ls", pwzPrint);
1635 }
1636
1637 WcaLog(LOGMSG_STANDARD, "--- End Table Dump %ls ---", wzTable);
1638
1639LExit:
1640 ReleaseStr(pwzPrint);
1641 ReleaseStr(pwzData);
1642 ReleaseStr(pwzQuery);
1643
1644 return hr;
1645}
1646
1647
1648HRESULT WIXAPI WcaDeferredActionRequiresReboot()
1649{
1650 HRESULT hr = S_OK;
1651 ATOM atomReboot = 0;
1652
1653 atomReboot = ::GlobalAddAtomW(L"WcaDeferredActionRequiresReboot");
1654 ExitOnNullWithLastError(atomReboot, hr, "Failed to create WcaDeferredActionRequiresReboot global atom.");
1655
1656LExit:
1657 return hr;
1658}
1659
1660
1661BOOL WIXAPI WcaDidDeferredActionRequireReboot()
1662{
1663 // NOTE: This function does not delete the global atom. That is done
1664 // purposefully so that any other installs that occur after this point also
1665 // require a reboot.
1666 ATOM atomReboot = ::GlobalFindAtomW(L"WcaDeferredActionRequiresReboot");
1667 return 0 != atomReboot;
1668}
diff --git a/src/libs/wcautil/WixToolset.WcaUtil/wcawrapquery.cpp b/src/libs/wcautil/WixToolset.WcaUtil/wcawrapquery.cpp
new file mode 100644
index 00000000..a3b593fd
--- /dev/null
+++ b/src/libs/wcautil/WixToolset.WcaUtil/wcawrapquery.cpp
@@ -0,0 +1,717 @@
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 "wcawrapquery.h"
5
6static const LPWSTR ISINSTALLEDCOLUMNNAME = L"ISInstalled";
7static const LPWSTR ISACTIONCOLUMNNAME = L"ISAction";
8static const LPWSTR SOURCEPATHCOLUMNNAME = L"SourcePath";
9static const LPWSTR TARGETPATHCOLUMNNAME = L"TargetPath";
10
11// This instantiates a new query object in the deferred CA, and returns the handle to the query
12WCA_WRAPQUERY_HANDLE WIXAPI GetNewQueryInstance(
13 DWORD dwInColumns,
14 DWORD dwInRows
15 )
16{
17 HRESULT hr = S_OK;
18
19 WCA_WRAPQUERY_HANDLE hNewHandle = NULL;
20
21 hNewHandle = static_cast<WCA_WRAPQUERY_HANDLE>(MemAlloc(sizeof(WCA_WRAPQUERY_STRUCT), TRUE));
22 if (NULL == hNewHandle)
23 {
24 hr = E_OUTOFMEMORY;
25 ExitOnFailure(hr, "Failed to allocate Query Instance");
26 }
27
28 // Initialize non-array members
29 hNewHandle->dwColumns = dwInColumns;
30 hNewHandle->dwRows = dwInRows;
31 hNewHandle->dwNextIndex = 0;
32
33 // Initialize arrays
34 if (0 != hNewHandle->dwColumns)
35 {
36 hNewHandle->pcdtColumnType = static_cast<eColumnDataType *>(MemAlloc(hNewHandle->dwColumns * sizeof(eColumnDataType), TRUE));
37 if (NULL == hNewHandle->pcdtColumnType)
38 {
39 hr = E_OUTOFMEMORY;
40 ExitOnFailure(hr, "Failed to allocate column type array");
41 }
42
43 hNewHandle->ppwzColumnNames = static_cast<LPWSTR *>(MemAlloc(hNewHandle->dwColumns * sizeof(LPWSTR), TRUE));
44 if (NULL == hNewHandle->ppwzColumnNames)
45 {
46 hr = E_OUTOFMEMORY;
47 ExitOnFailure(hr, "Failed to allocate column names array");
48 }
49 }
50
51 for (DWORD i=0;i<hNewHandle->dwColumns;i++)
52 {
53 hNewHandle->pcdtColumnType[i] = cdtUnknown;
54 hNewHandle->ppwzColumnNames[i] = NULL;
55 }
56
57 if (0 != hNewHandle->dwRows)
58 {
59 hNewHandle->phRecords = static_cast<MSIHANDLE *>(MemAlloc(hNewHandle->dwRows * sizeof(MSIHANDLE), TRUE));
60 if (NULL == hNewHandle->phRecords)
61 {
62 hr = E_OUTOFMEMORY;
63 ExitOnFailure(hr, "Failed to allocate records array");
64 }
65 }
66
67 for (DWORD i=0;i<hNewHandle->dwRows;i++)
68 {
69 hNewHandle->phRecords[i] = NULL;
70 }
71
72 return hNewHandle;
73
74LExit:
75 // The handle isn't complete, so destroy any memory it allocated before returning NULL
76 if (NULL != hNewHandle)
77 {
78 WcaFinishUnwrapQuery(hNewHandle);
79 }
80
81 return NULL;
82}
83
84// This function takes in the column type string from MsiViewGetColumnInfo, and returns
85// whether the column stores strings, ints, binary streams, or
86// cdtUnknown if this information couldn't be determined.
87eColumnDataType WIXAPI GetDataTypeFromString(
88 LPCWSTR pwzTypeString
89 )
90{
91 if (!pwzTypeString || !*pwzTypeString)
92 {
93 return cdtUnknown;
94 }
95
96 switch (pwzTypeString[0])
97 {
98 case 'v':
99 case 'V':
100 case 'o':
101 case 'O':
102 return cdtStream;
103
104 case 'g':
105 case 'G':
106 case 's':
107 case 'S':
108 case 'l':
109 case 'L':
110 return cdtString;
111
112 case 'i':
113 case 'I':
114 case 'j':
115 case 'J':
116 return cdtInt;
117
118 default:
119 return cdtUnknown;
120 }
121}
122
123HRESULT WIXAPI WcaWrapEmptyQuery(
124 __inout LPWSTR * ppwzCustomActionData
125 )
126{
127 HRESULT hr = S_OK;
128
129 WcaLog(LOGMSG_TRACEONLY, "Wrapping result of empty query");
130
131 hr = WcaWriteIntegerToCaData(static_cast<int>(wqaTableBegin), ppwzCustomActionData);
132 ExitOnFailure(hr, "Failed to write table begin marker to custom action data");
133
134 hr = WcaWriteIntegerToCaData(0, ppwzCustomActionData);
135 ExitOnFailure(hr, "Failed to write number of columns to custom action data");
136
137 hr = WcaWriteIntegerToCaData(0, ppwzCustomActionData);
138 ExitOnFailure(hr, "Failed to write number of rows to custom action data");
139
140 hr = WcaWriteIntegerToCaData(static_cast<int>(wqaTableFinish), ppwzCustomActionData);
141 ExitOnFailure(hr, "Failed to write table finish marker to custom action data");
142
143// WcaLog(LOGMSG_TRACEONLY, "Finished wrapping result of empty query");
144
145LExit:
146 return hr;
147}
148
149/********************************************************************
150WcaWrapQuery() - wraps a view and transmits it through the
151 CustomActionData property
152
153********************************************************************/
154HRESULT WIXAPI WcaWrapQuery(
155 __in_z LPCWSTR pwzQuery,
156 __inout LPWSTR * ppwzCustomActionData,
157 __in_opt DWORD dwFormatMask,
158 __in_opt DWORD dwComponentColumn,
159 __in_opt DWORD dwDirectoryColumn
160 )
161{
162 HRESULT hr = S_OK;
163 HRESULT hrTemp = S_OK;
164 UINT er = ERROR_SUCCESS;
165 UINT cViewColumns;
166 eColumnDataType *pcdtColumnTypeList = NULL;
167 LPWSTR pwzData = NULL;
168 LPWSTR pwzColumnData = NULL;
169 LPWSTR pwzRecordData = NULL;
170 BYTE* pbData = NULL;
171 DWORD dwNumRecords = 0;
172 BOOL fAddComponentState = FALSE; // Add two integer columns to the right side of the query - ISInstalled, and ISAction
173 BOOL fAddDirectoryPath = FALSE; // Add two string columns to the right side of the query - SourcePath, and TargetPath
174 int iTempInteger = 0;
175
176 WCHAR wzPath[MAX_PATH + 1];
177 DWORD dwLen;
178 INSTALLSTATE isInstalled = INSTALLSTATE_UNKNOWN;
179 INSTALLSTATE isAction = INSTALLSTATE_UNKNOWN;
180
181 PMSIHANDLE hColumnTypes, hColumnNames;
182 PMSIHANDLE hView, hRec;
183
184 WcaLog(LOGMSG_TRACEONLY, "Wrapping result of query: \"%ls\"", pwzQuery);
185
186 // open the view
187 hr = WcaOpenExecuteView(pwzQuery, &hView);
188 ExitOnFailure(hr, "Failed to execute view");
189
190 hr = WcaWriteIntegerToCaData(static_cast<int>(wqaTableBegin), ppwzCustomActionData);
191 ExitOnFailure(hr, "Failed to write table begin marker to custom action data");
192
193// WcaLog(LOGMSG_TRACEONLY, "Starting to wrap table's column information", pwzQuery);
194
195 // Use GetColumnInfo to populate the names of the columns.
196 er = ::MsiViewGetColumnInfo(hView, MSICOLINFO_TYPES, &hColumnTypes);
197 ExitOnWin32Error(er, hr, "Failed to get column types");
198
199 er = ::MsiViewGetColumnInfo(hView, MSICOLINFO_NAMES, &hColumnNames);
200 ExitOnWin32Error(er, hr, "Failed to get column names");
201
202 cViewColumns = ::MsiRecordGetFieldCount(hColumnTypes);
203
204 if (0xFFFFFFFF == cViewColumns)
205 {
206 // According to MSDN, this return value only happens when the handle is invalid
207 hr = E_HANDLE;
208 ExitOnFailure(hr, "Failed to get number of fields in record");
209 }
210
211 if (cViewColumns >= dwComponentColumn)
212 {
213 fAddComponentState = TRUE;
214 }
215 else if (0xFFFFFFFF != dwComponentColumn)
216 {
217 hr = E_INVALIDARG;
218 ExitOnFailure(hr, "Component column %d out of range", dwComponentColumn);
219 }
220
221 if (cViewColumns >= dwDirectoryColumn)
222 {
223 fAddDirectoryPath = TRUE;
224 }
225 else if (0xFFFFFFFF != dwDirectoryColumn)
226 {
227 hr = E_INVALIDARG;
228 ExitOnFailure(hr, "Directory column %d out of range", dwDirectoryColumn);
229 }
230
231 hr = WcaWriteIntegerToCaData(static_cast<int>(cViewColumns) + 2 * static_cast<int>(fAddComponentState) + 2 * static_cast<int>(fAddDirectoryPath), ppwzCustomActionData);
232 ExitOnFailure(hr, "Failed to write number of columns to custom action data");
233
234 pcdtColumnTypeList = new eColumnDataType[cViewColumns];
235 ExitOnNull(pcdtColumnTypeList, hr, E_OUTOFMEMORY, "Failed to allocate memory to store column info types");
236
237 // Loop through all the columns reporting information about each one
238 for (DWORD i = 0; i < cViewColumns; i++)
239 {
240 hr = WcaGetRecordString(hColumnNames, i+1, &pwzData);
241 ExitOnFailure(hr, "Failed to get the column %d name", i+1);
242
243 hr = WcaWriteStringToCaData(pwzData, &pwzColumnData);
244 ExitOnFailure(hr, "Failed to write column %d name %ls to custom action data", i+1, pwzData);
245
246 hr = WcaGetRecordString(hColumnTypes, i+1, &pwzData);
247 ExitOnFailure(hr, "Failed to get the column type string for column %d", i+1);
248
249 pcdtColumnTypeList[i] = GetDataTypeFromString(pwzData);
250
251 if (cdtUnknown == pcdtColumnTypeList[i])
252 {
253 hr = E_INVALIDARG;
254 ExitOnFailure(hr, "Failed to recognize column %d type string: %ls", i+1, pwzData);
255 }
256
257 hr = WcaWriteIntegerToCaData(pcdtColumnTypeList[i], &pwzColumnData);
258 ExitOnFailure(hr, "Failed to write column %d type enumeration to custom action data", i+1);
259 }
260
261 // Add two integer columns to the right side of the query - ISInstalled, and ISAction
262 if (fAddComponentState)
263 {
264 hr = WcaWriteStringToCaData(ISINSTALLEDCOLUMNNAME, &pwzColumnData);
265 ExitOnFailure(hr, "Failed to write extra column %d name %ls to custom action data", cViewColumns + 1, ISINSTALLEDCOLUMNNAME);
266
267 hr = WcaWriteIntegerToCaData(cdtInt, &pwzColumnData);
268 ExitOnFailure(hr, "Failed to write extra column %d type to custom action data", cViewColumns + 1);
269
270 hr = WcaWriteStringToCaData(ISACTIONCOLUMNNAME, &pwzColumnData);
271 ExitOnFailure(hr, "Failed to write extra column %d name %ls to custom action data", cViewColumns + 1, ISACTIONCOLUMNNAME);
272
273 hr = WcaWriteIntegerToCaData(cdtInt, &pwzColumnData);
274 ExitOnFailure(hr, "Failed to write extra column %d type to custom action data", cViewColumns + 1);
275 }
276
277 if (fAddDirectoryPath)
278 {
279 hr = WcaWriteStringToCaData(SOURCEPATHCOLUMNNAME, &pwzColumnData);
280 ExitOnFailure(hr, "Failed to write extra column %d name %ls to custom action data", cViewColumns + 1, SOURCEPATHCOLUMNNAME);
281
282 hr = WcaWriteIntegerToCaData(cdtString, &pwzColumnData);
283 ExitOnFailure(hr, "Failed to write extra column %d type to custom action data", cViewColumns + 1);
284
285 hr = WcaWriteStringToCaData(TARGETPATHCOLUMNNAME, &pwzColumnData);
286 ExitOnFailure(hr, "Failed to write extra column %d name %ls to custom action data", cViewColumns + 1, TARGETPATHCOLUMNNAME);
287
288 hr = WcaWriteIntegerToCaData(cdtString, &pwzColumnData);
289 ExitOnFailure(hr, "Failed to write extra column %d type to custom action data", cViewColumns + 1);
290 }
291
292 // Begin wrapping actual table data
293 //WcaLog(LOGMSG_TRACEONLY, "Starting to wrap table data", pwzQuery);
294 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
295 {
296 hr = WcaWriteIntegerToCaData(static_cast<int>(wqaRowBegin), &pwzRecordData);
297 ExitOnFailure(hr, "Failed to write row begin marker to custom action data");
298
299 for (DWORD i = 0; i < cViewColumns; i++)
300 {
301 switch (pcdtColumnTypeList[i])
302 {
303 case cdtString:
304 // If we were given a format mask, we're not past the index, and it's set to true for this column, then format the string
305 if (i < (sizeof(dwFormatMask) * 8) && (dwFormatMask & (1 << i)))
306 {
307 hr = WcaGetRecordFormattedString(hRec, i + 1, &pwzData);
308 }
309 else
310 {
311 hr = WcaGetRecordString(hRec, i + 1, &pwzData);
312 }
313 ExitOnFailure(hr, "Failed to get string for column %d", i + 1);
314
315 hr = WcaWriteStringToCaData(pwzData, &pwzRecordData);
316 ExitOnFailure(hr, "Failed to write string to temporary record custom action data for column %d", i + 1);
317 break;
318
319 case cdtInt:
320 if (i < (sizeof(dwFormatMask) * 8) && (dwFormatMask & (1 << i)))
321 {
322 hr = WcaGetRecordFormattedInteger(hRec, i + 1, &iTempInteger);
323 }
324 else
325 {
326 hr = WcaGetRecordInteger(hRec, i + 1, &iTempInteger);
327 }
328 ExitOnFailure(hr, "Failed to get integer for column %d", i + 1);
329
330 hr = WcaWriteIntegerToCaData(iTempInteger, &pwzRecordData);
331 ExitOnFailure(hr, "Failed to write integer to temporary record custom action data for column %d", i + 1);
332 break;
333
334 case cdtStream:
335 hr = E_NOTIMPL;
336 ExitOnFailure(hr, "A query was wrapped which contained a binary stream data field in column %d - however, the ability to wrap stream data fields is not implemented at this time", i);
337 break;
338
339 case cdtUnknown:
340 default:
341 hr = E_INVALIDARG;
342 ExitOnFailure(hr, "Failed to recognize column type enumeration %d for column %d", pcdtColumnTypeList[i], i + 1);
343 }
344 }
345
346 // Add two integer columns to the right side of the query - ISInstalled, and ISAction
347 if (fAddComponentState)
348 {
349 // Get the component ID
350 hr = WcaGetRecordString(hRec, dwComponentColumn, &pwzData);
351 ExitOnFailure(hr, "Failed to get component from column %d while adding extra columns", dwComponentColumn);
352
353 if (!pwzData || !*pwzData)
354 {
355 // If no component was provided, set these both to zero as though a structure to store them were allocated with memory zero'd out
356 isInstalled = (INSTALLSTATE)0;
357 isAction = (INSTALLSTATE)0;
358 }
359 else
360 {
361 er = ::MsiGetComponentStateW(WcaGetInstallHandle(), pwzData, &isInstalled, &isAction);
362 // If we don't get the component state, that may be because the component ID was invalid, but isn't necessarily an error, so write NULL's
363 if (FAILED(HRESULT_FROM_WIN32(er)))
364 {
365 ExitOnFailure(hr, "Failed to get component state for component %ls", pwzData);
366 }
367 }
368
369 hr = WcaWriteIntegerToCaData(isInstalled, &pwzRecordData);
370 ExitOnFailure(hr, "Failed to write extra ISInstalled column to custom action data");
371
372 hr = WcaWriteIntegerToCaData(isAction, &pwzRecordData);
373 ExitOnFailure(hr, "Failed to write extra ISAction column to custom action data");
374 }
375
376 // Add two string columns to the right side of the query - SourcePath, and TargetPath
377 if (fAddDirectoryPath)
378 {
379 hr = WcaGetRecordString(hRec, dwDirectoryColumn, &pwzData);
380 // If this fails, ignore it, and just leave those columns blank
381 if (SUCCEEDED(hr))
382 {
383 // Only get source path if the component state is INSTALLSTATE_SOURCE, or if we have no component to check the installstate of
384 if (INSTALLSTATE_SOURCE == isAction || !fAddComponentState)
385 {
386 dwLen = countof(wzPath);
387 er = ::MsiGetSourcePathW(WcaGetInstallHandle(), pwzData, wzPath, &dwLen);
388 hrTemp = HRESULT_FROM_WIN32(er);
389 if (dwLen > countof(wzPath))
390 {
391 hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
392 ExitOnRootFailure(hr, "Failed to record entire Source Path for Directory %ls because its length was greater than MAX_PATH.", pwzData);
393 }
394
395 if (SUCCEEDED(hrTemp))
396 {
397 hr = WcaWriteStringToCaData(wzPath, &pwzRecordData);
398 ExitOnFailure(hr, "Failed to write source path string to record data string");
399 }
400 else
401 {
402 hr = WcaWriteStringToCaData(L"", &pwzRecordData);
403 ExitOnFailure(hr, "Failed to write empty source path string to record data string");
404 }
405 }
406 else
407 {
408 hr = WcaWriteStringToCaData(L"", &pwzRecordData);
409 ExitOnFailure(hr, "Failed to write empty source path string before writing target path string to record data string");
410 }
411
412 dwLen = countof(wzPath);
413 er = ::MsiGetTargetPathW(WcaGetInstallHandle(), pwzData, wzPath, &dwLen);
414 hrTemp = HRESULT_FROM_WIN32(er);
415 if (dwLen > countof(wzPath))
416 {
417 hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
418 ExitOnRootFailure(hr, "Failed to record entire Source Path for Directory %ls because its length was greater than MAX_PATH.", pwzData);
419 }
420 if (SUCCEEDED(hrTemp))
421 {
422 hr = WcaWriteStringToCaData(wzPath, &pwzRecordData);
423 ExitOnFailure(hr, "Failed to write target path string to record data string");
424 }
425 else
426 {
427 hr = WcaWriteStringToCaData(L"", &pwzRecordData);
428 ExitOnFailure(hr, "Failed to write empty target path string to record data string");
429 }
430 }
431 else
432 {
433 // Write both fields as blank
434 hr = WcaWriteStringToCaData(L"", &pwzRecordData);
435 hr = WcaWriteStringToCaData(L"", &pwzRecordData);
436 }
437 }
438
439 hr = WcaWriteIntegerToCaData(static_cast<int>(wqaRowFinish), &pwzRecordData);
440 ExitOnFailure(hr, "Failed to write row finish marker to custom action data");
441
442 ++dwNumRecords;
443 }
444
445 hr = WcaWriteIntegerToCaData(dwNumRecords, ppwzCustomActionData);
446 ExitOnFailure(hr, "Failed to write number of records to custom action data");
447
448 if (NULL != pwzColumnData)
449 {
450 hr = WcaWriteStringToCaData(pwzColumnData, ppwzCustomActionData);
451 ExitOnFailure(hr, "Failed to write column data to custom action data");
452 }
453
454 if (NULL != pwzRecordData)
455 {
456 hr = WcaWriteStringToCaData(pwzRecordData, ppwzCustomActionData);
457 ExitOnFailure(hr, "Failed to write record data to custom action data");
458 }
459
460 hr = WcaWriteIntegerToCaData(static_cast<int>(wqaTableFinish), ppwzCustomActionData);
461 ExitOnFailure(hr, "Failed to write table finish marker to custom action data");
462
463// WcaLog(LOGMSG_TRACEONLY, "Finished wrapping result of query: \"%ls\"", pwzQuery);
464
465LExit:
466 ReleaseStr(pwzData);
467 ReleaseStr(pwzColumnData);
468 ReleaseStr(pwzRecordData);
469
470 ReleaseMem(pbData);
471
472 return hr;
473}
474
475
476/********************************************************************
477WcaBeginUnwrapQuery() - unwraps a view for direct access from the
478 CustomActionData property
479
480********************************************************************/
481HRESULT WIXAPI WcaBeginUnwrapQuery(
482 __out WCA_WRAPQUERY_HANDLE * phWrapQuery,
483 __inout LPWSTR * ppwzCustomActionData
484 )
485{
486 HRESULT hr = S_OK;
487 int iTempInteger = 0;
488 int iColumns = 0;
489 int iRows = 0;
490 BYTE* pbData = NULL;
491 LPWSTR pwzData = NULL;
492 WCA_WRAPQUERY_HANDLE hWrapQuery = NULL;
493
494 WcaLog(LOGMSG_TRACEONLY, "Unwrapping a query from custom action data");
495
496 hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iTempInteger);
497 if (wqaTableBegin != iTempInteger)
498 {
499 hr = E_INVALIDARG;
500 }
501 ExitOnFailure(hr, "Failed to read table begin marker from custom action data (read %d instead)", iTempInteger);
502
503 hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iColumns);
504 ExitOnFailure(hr, "Failed to read number of columns from custom action data");
505
506 hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iRows);
507 ExitOnFailure(hr, "Failed to read number of rows from custom action data");
508
509 hWrapQuery = GetNewQueryInstance(iColumns, iRows);
510 if (NULL == hWrapQuery)
511 {
512 hr = E_POINTER;
513 }
514 ExitOnFailure(hr, "Failed to get a query instance with %d columns and %d rows", iColumns, iRows);
515
516 for (int i = 0; i < iColumns; i++)
517 {
518 hr = WcaReadStringFromCaData(ppwzCustomActionData, &(hWrapQuery->ppwzColumnNames[i]));
519 ExitOnFailure(hr, "Failed to read column %d's name from custom action data", i+1);
520
521 hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iTempInteger);
522 if (cdtString != iTempInteger && cdtInt != iTempInteger && cdtStream != iTempInteger)
523 {
524 hr = E_INVALIDARG;
525 }
526 ExitOnFailure(hr, "Failed to read column %d's type from custom action data", i+1);
527
528 // Set the column type into the actual data structure
529 hWrapQuery->pcdtColumnType[i] = (eColumnDataType)iTempInteger;
530 }
531
532 for (int i = 0; i < iRows; i++)
533 {
534 hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iTempInteger);
535 if (wqaRowBegin != iTempInteger)
536 {
537 hr = E_INVALIDARG;
538 }
539 ExitOnFailure(hr, "Failed to read begin row marker from custom action data (read %d instead)", iTempInteger);
540
541 hWrapQuery->phRecords[i] = ::MsiCreateRecord((unsigned int)iColumns);
542
543 for (int j = 0; j < iColumns; j++)
544 {
545 switch (hWrapQuery->pcdtColumnType[j])
546 {
547 case cdtString:
548 hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzData);
549 ExitOnFailure(hr, "Failed to read string from custom action data");
550
551 hr = WcaSetRecordString(hWrapQuery->phRecords[i], j+1, pwzData);
552 ExitOnFailure(hr, "Failed to write string %ls to record in column %d", pwzData, j+1);
553 break;
554
555 case cdtInt:
556 WcaReadIntegerFromCaData(ppwzCustomActionData, &iTempInteger);
557 ExitOnFailure(hr, "Failed to read integer from custom action data");
558
559 WcaSetRecordInteger(hWrapQuery->phRecords[i], j+1, iTempInteger);
560 ExitOnFailure(hr, "Failed to write integer %d to record in column %d", iTempInteger, j+1);
561 break;
562
563 case cdtStream:
564 hr = E_NOTIMPL;
565 ExitOnFailure(hr, "A query was wrapped which contained a stream data field - however, the ability to wrap stream data fields is not implemented at this time");
566 break;
567
568 case cdtUnknown:
569 default:
570 hr = E_INVALIDARG;
571 ExitOnFailure(hr, "Failed to recognize column type enumeration %d for column %d", hWrapQuery->pcdtColumnType[j+1], i+1);
572 }
573 }
574
575 hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iTempInteger);
576 if (wqaRowFinish != iTempInteger)
577 {
578 hr = E_INVALIDARG;
579 }
580 ExitOnFailure(hr, "Failed to read row finish marker from custom action data (read %d instead)", iTempInteger);
581 }
582
583 hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iTempInteger);
584 if (wqaTableFinish != iTempInteger)
585 {
586 hr = E_INVALIDARG;
587 }
588 ExitOnFailure(hr, "Failed to read table finish marker from custom action data (read %d instead)", iTempInteger);
589
590 *phWrapQuery = hWrapQuery;
591
592// WcaLog(LOGMSG_TRACEONLY, "Successfully finished unwrapping a query from custom action data");
593
594LExit:
595 ReleaseStr(pwzData);
596 ReleaseMem(pbData);
597
598 return hr;
599}
600
601// This function returns the total number of records in a query
602DWORD WIXAPI WcaGetQueryRecords(
603 __in const WCA_WRAPQUERY_HANDLE hWrapQuery
604 )
605{
606 return hWrapQuery->dwRows;
607}
608
609// This function resets a query back to its first row, so that the next fetch returns the first record
610void WIXAPI WcaFetchWrappedReset(
611 __in WCA_WRAPQUERY_HANDLE hWrapQuery
612 )
613{
614 hWrapQuery->dwNextIndex = 0;
615}
616
617// Fetches the next record in the query
618// NOTE: the MSIHANDLE returned by this function should not be released, as it is the same handle used by the query object to maintain the item.
619// so, don't use this function with PMSIHANDLE objects!
620HRESULT WIXAPI WcaFetchWrappedRecord(
621 __in WCA_WRAPQUERY_HANDLE hWrapQuery,
622 __out MSIHANDLE* phRec
623 )
624{
625 DWORD dwNextIndex = hWrapQuery->dwNextIndex;
626
627 if (dwNextIndex >= hWrapQuery->dwRows)
628 {
629 return E_NOMOREITEMS;
630 }
631
632 if (NULL == hWrapQuery->phRecords[dwNextIndex])
633 {
634 return E_HANDLE;
635 }
636
637 *phRec = hWrapQuery->phRecords[hWrapQuery->dwNextIndex];
638
639 // Increment our next index variable
640 ++hWrapQuery->dwNextIndex;
641
642 return S_OK;
643}
644
645// Fetch the next record in the query where the string value in column dwComparisonColumn equals the value pwzExpectedValue
646// NOTE: the MSIHANDLE returned by this function should not be released, as it is the same handle used by the query object to maintain the item.
647// so, don't use this function with PMSIHANDLE objects!
648HRESULT WIXAPI WcaFetchWrappedRecordWhereString(
649 __in WCA_WRAPQUERY_HANDLE hWrapQuery,
650 __in DWORD dwComparisonColumn,
651 __in_z LPCWSTR pwzExpectedValue,
652 __out MSIHANDLE* phRec
653 )
654{
655 HRESULT hr = S_OK;
656 MSIHANDLE hRec = NULL;
657 LPWSTR pwzData = NULL;
658
659 while (S_OK == (hr = WcaFetchWrappedRecord(hWrapQuery, &hRec)))
660 {
661 ExitOnFailure(hr, "Failed to fetch a wrapped record");
662
663 hr = WcaGetRecordString(hRec, dwComparisonColumn, &pwzData);
664 ExitOnFailure(hr, "Failed to get record string in column %d", dwComparisonColumn);
665
666 if (0 == lstrcmpW(pwzData, pwzExpectedValue))
667 {
668 *phRec = hRec;
669 ExitFunction1(hr = S_OK);
670 }
671 }
672 // If we errored here but not because there were no records left, write an error to the log
673 if (hr != E_NOMOREITEMS)
674 {
675 ExitOnFailure(hr, "Failed while searching for a wrapped record where column %d is set to %ls", dwComparisonColumn, pwzExpectedValue);
676 }
677
678LExit:
679 ReleaseStr(pwzData);
680
681 return hr;
682}
683
684/********************************************************************
685WcaBeginUnwrapQuery() - Finishes unwrapping a view for direct access
686 from the CustomActionData property
687
688********************************************************************/
689void WIXAPI WcaFinishUnwrapQuery(
690 __in_opt WCA_WRAPQUERY_HANDLE hWrapQuery
691 )
692{
693 if (NULL == hWrapQuery)
694 {
695 WcaLog(LOGMSG_TRACEONLY, "Failed to finish an unwrap query - ignoring");
696 return;
697 }
698
699 ReleaseMem(hWrapQuery->pcdtColumnType);
700
701 for (DWORD i=0;i<hWrapQuery->dwColumns;i++)
702 {
703 ReleaseStr(hWrapQuery->ppwzColumnNames[i]);
704 }
705 ReleaseMem(hWrapQuery->ppwzColumnNames);
706
707 for (DWORD i=0;i<hWrapQuery->dwRows;i++)
708 {
709 if (NULL != hWrapQuery->phRecords[i])
710 {
711 ::MsiCloseHandle(hWrapQuery->phRecords[i]);
712 }
713 }
714 ReleaseMem(hWrapQuery->phRecords);
715
716 ReleaseMem(hWrapQuery);
717}
diff --git a/src/libs/wcautil/appveyor.cmd b/src/libs/wcautil/appveyor.cmd
new file mode 100644
index 00000000..a2596256
--- /dev/null
+++ b/src/libs/wcautil/appveyor.cmd
@@ -0,0 +1,20 @@
1@setlocal
2@pushd %~dp0
3
4nuget restore || exit /b
5
6msbuild -p:Configuration=Release;Platform=x86;PlatformToolset=v140 || exit /b
7msbuild -p:Configuration=Release;Platform=x64;PlatformToolset=v140 || exit /b
8
9msbuild -p:Configuration=Release;Platform=x86;PlatformToolset=v141 || exit /b
10msbuild -p:Configuration=Release;Platform=x64;PlatformToolset=v141 || exit /b
11msbuild -p:Configuration=Release;Platform=ARM64;PlatformToolset=v141 || exit /b
12
13msbuild -p:Configuration=Release;Platform=x86;PlatformToolset=v142 || exit /b
14msbuild -p:Configuration=Release;Platform=x64;PlatformToolset=v142 || exit /b
15msbuild -p:Configuration=Release;Platform=ARM64;PlatformToolset=v142 || exit /b
16
17msbuild -p:Configuration=Release -t:PackNativeNuget src\wcautil\wcautil.vcxproj || exit /b
18
19@popd
20@endlocal \ No newline at end of file
diff --git a/src/libs/wcautil/appveyor.yml b/src/libs/wcautil/appveyor.yml
new file mode 100644
index 00000000..bbf880f0
--- /dev/null
+++ b/src/libs/wcautil/appveyor.yml
@@ -0,0 +1,35 @@
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
6image: Visual Studio 2019
7
8version: 0.0.0.{build}
9configuration: Release
10
11environment:
12 DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
13 DOTNET_CLI_TELEMETRY_OPTOUT: 1
14 NUGET_XMLDOC_MODE: skip
15
16build_script:
17 - appveyor.cmd
18
19pull_requests:
20 do_not_increment_build_number: true
21
22nuget:
23 disable_publish_on_pr: true
24
25skip_branch_with_pr: true
26skip_tags: true
27
28artifacts:
29- path: build\Release\**\*.nupkg
30 name: nuget
31
32notifications:
33- provider: Slack
34 incoming_webhook:
35 secure: p5xuu+4x2JHfwGDMDe5KcG1k7gZxqYc4jWVwvyNZv5cvkubPD2waJs5yXMAXZNN7Z63/3PWHb7q4KoY/99AjauYa1nZ4c5qYqRPFRBKTHfA=
diff --git a/src/libs/wcautil/nuget.config b/src/libs/wcautil/nuget.config
new file mode 100644
index 00000000..790be2b0
--- /dev/null
+++ b/src/libs/wcautil/nuget.config
@@ -0,0 +1,8 @@
1<?xml version="1.0" encoding="utf-8"?>
2<configuration>
3 <packageSources>
4 <clear />
5 <add key="wixtoolset-dutil" value="https://ci.appveyor.com/nuget/wixtoolset-dutil" />
6 <add key="api.nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
7 </packageSources>
8</configuration> \ No newline at end of file
diff --git a/src/libs/wcautil/wcautil.sln b/src/libs/wcautil/wcautil.sln
new file mode 100644
index 00000000..5682f1f7
--- /dev/null
+++ b/src/libs/wcautil/wcautil.sln
@@ -0,0 +1,37 @@
1
2Microsoft Visual Studio Solution File, Format Version 12.00
3# Visual Studio Version 16
4VisualStudioVersion = 16.0.31005.135
5MinimumVisualStudioVersion = 15.0.26124.0
6Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wcautil", "src\wcautil\wcautil.vcxproj", "{5B3714B6-3A76-463E-8595-D48DA276C512}"
7EndProject
8Global
9 GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 Debug|ARM64 = Debug|ARM64
11 Debug|x64 = Debug|x64
12 Debug|x86 = Debug|x86
13 Release|ARM64 = Release|ARM64
14 Release|x64 = Release|x64
15 Release|x86 = Release|x86
16 EndGlobalSection
17 GlobalSection(ProjectConfigurationPlatforms) = postSolution
18 {5B3714B6-3A76-463E-8595-D48DA276C512}.Debug|ARM64.ActiveCfg = Debug|ARM64
19 {5B3714B6-3A76-463E-8595-D48DA276C512}.Debug|ARM64.Build.0 = Debug|ARM64
20 {5B3714B6-3A76-463E-8595-D48DA276C512}.Debug|x64.ActiveCfg = Debug|x64
21 {5B3714B6-3A76-463E-8595-D48DA276C512}.Debug|x64.Build.0 = Debug|x64
22 {5B3714B6-3A76-463E-8595-D48DA276C512}.Debug|x86.ActiveCfg = Debug|Win32
23 {5B3714B6-3A76-463E-8595-D48DA276C512}.Debug|x86.Build.0 = Debug|Win32
24 {5B3714B6-3A76-463E-8595-D48DA276C512}.Release|ARM64.ActiveCfg = Release|ARM64
25 {5B3714B6-3A76-463E-8595-D48DA276C512}.Release|ARM64.Build.0 = Release|ARM64
26 {5B3714B6-3A76-463E-8595-D48DA276C512}.Release|x64.ActiveCfg = Release|x64
27 {5B3714B6-3A76-463E-8595-D48DA276C512}.Release|x64.Build.0 = Release|x64
28 {5B3714B6-3A76-463E-8595-D48DA276C512}.Release|x86.ActiveCfg = Release|Win32
29 {5B3714B6-3A76-463E-8595-D48DA276C512}.Release|x86.Build.0 = Release|Win32
30 EndGlobalSection
31 GlobalSection(SolutionProperties) = preSolution
32 HideSolutionNode = FALSE
33 EndGlobalSection
34 GlobalSection(ExtensibilityGlobals) = postSolution
35 SolutionGuid = {DD209744-C40E-4C34-8CB4-BC6B71F9A133}
36 EndGlobalSection
37EndGlobal