@joebigelow / wix / commits / 7df142a4

Initialize repo

Sean Hall committed Sep 3, 2017 at 17:25 UTC 7df142a4586bcede1442feb38029ff15556ccb46
29 files changed +5298
.gitattributes new
+2
@@ -0,0 +1,2 @@
1 +# Auto detect text files and perform LF normalization
2 +* text=auto
.gitignore new
+295
@@ -0,0 +1,295 @@
1 +## Ignore Visual Studio temporary files, build results, and
2 +## files generated by popular Visual Studio add-ons.
3 +##
4 +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 +
6 +# User-specific files
7 +*.suo
8 +*.user
9 +*.userosscache
10 +*.sln.docstates
11 +
12 +# User-specific files (MonoDevelop/Xamarin Studio)
13 +*.userprefs
14 +
15 +# Build results
16 +[Dd]ebug/
17 +[Dd]ebugPublic/
18 +[Rr]elease/
19 +[Rr]eleases/
20 +x64/
21 +x86/
22 +bld/
23 +[Bb]in/
24 +[Oo]bj/
25 +[Ll]og/
26 +
27 +# Visual Studio 2015 cache/options directory
28 +.vs/
29 +# Uncomment if you have tasks that create the project's static files in wwwroot
30 +#wwwroot/
31 +
32 +# MSTest test Results
33 +[Tt]est[Rr]esult*/
34 +[Bb]uild[Ll]og.*
35 +
36 +# NUNIT
37 +*.VisualState.xml
38 +TestResult.xml
39 +
40 +# Build Results of an ATL Project
41 +[Dd]ebugPS/
42 +[Rr]eleasePS/
43 +dlldata.c
44 +
45 +# Benchmark Results
46 +BenchmarkDotNet.Artifacts/
47 +
48 +# .NET Core
49 +project.lock.json
50 +project.fragment.lock.json
51 +artifacts/
52 +**/Properties/launchSettings.json
53 +
54 +*_i.c
55 +*_p.c
56 +*_i.h
57 +*.ilk
58 +*.meta
59 +*.obj
60 +*.pch
61 +*.pdb
62 +*.pgc
63 +*.pgd
64 +*.rsp
65 +*.sbr
66 +*.tlb
67 +*.tli
68 +*.tlh
69 +*.tmp
70 +*.tmp_proj
71 +*.log
72 +*.vspscc
73 +*.vssscc
74 +.builds
75 +*.pidb
76 +*.svclog
77 +*.scc
78 +
79 +# Chutzpah Test files
80 +_Chutzpah*
81 +
82 +# Visual C++ cache files
83 +ipch/
84 +*.aps
85 +*.ncb
86 +*.opendb
87 +*.opensdf
88 +*.sdf
89 +*.cachefile
90 +*.VC.db
91 +*.VC.VC.opendb
92 +
93 +# Visual Studio profiler
94 +*.psess
95 +*.vsp
96 +*.vspx
97 +*.sap
98 +
99 +# TFS 2012 Local Workspace
100 +$tf/
101 +
102 +# Guidance Automation Toolkit
103 +*.gpState
104 +
105 +# ReSharper is a .NET coding add-in
106 +_ReSharper*/
107 +*.[Rr]e[Ss]harper
108 +*.DotSettings.user
109 +
110 +# JustCode is a .NET coding add-in
111 +.JustCode
112 +
113 +# TeamCity is a build add-in
114 +_TeamCity*
115 +
116 +# DotCover is a Code Coverage Tool
117 +*.dotCover
118 +
119 +# Visual Studio code coverage results
120 +*.coverage
121 +*.coveragexml
122 +
123 +# NCrunch
124 +_NCrunch_*
125 +.*crunch*.local.xml
126 +nCrunchTemp_*
127 +
128 +# MightyMoose
129 +*.mm.*
130 +AutoTest.Net/
131 +
132 +# Web workbench (sass)
133 +.sass-cache/
134 +
135 +# Installshield output folder
136 +[Ee]xpress/
137 +
138 +# DocProject is a documentation generator add-in
139 +DocProject/buildhelp/
140 +DocProject/Help/*.HxT
141 +DocProject/Help/*.HxC
142 +DocProject/Help/*.hhc
143 +DocProject/Help/*.hhk
144 +DocProject/Help/*.hhp
145 +DocProject/Help/Html2
146 +DocProject/Help/html
147 +
148 +# Click-Once directory
149 +publish/
150 +
151 +# Publish Web Output
152 +*.[Pp]ublish.xml
153 +*.azurePubxml
154 +# Note: Comment the next line if you want to checkin your web deploy settings,
155 +# but database connection strings (with potential passwords) will be unencrypted
156 +*.pubxml
157 +*.publishproj
158 +
159 +# Microsoft Azure Web App publish settings. Comment the next line if you want to
160 +# checkin your Azure Web App publish settings, but sensitive information contained
161 +# in these scripts will be unencrypted
162 +PublishScripts/
163 +
164 +# NuGet Packages
165 +*.nupkg
166 +# The packages folder can be ignored because of Package Restore
167 +**/packages/*
168 +# except build/, which is used as an MSBuild target.
169 +!**/packages/build/
170 +# Uncomment if necessary however generally it will be regenerated when needed
171 +#!**/packages/repositories.config
172 +# NuGet v3's project.json files produces more ignorable files
173 +*.nuget.props
174 +*.nuget.targets
175 +
176 +# Microsoft Azure Build Output
177 +csx/
178 +*.build.csdef
179 +
180 +# Microsoft Azure Emulator
181 +ecf/
182 +rcf/
183 +
184 +# Windows Store app package directories and files
185 +AppPackages/
186 +BundleArtifacts/
187 +Package.StoreAssociation.xml
188 +_pkginfo.txt
189 +*.appx
190 +
191 +# Visual Studio cache files
192 +# files ending in .cache can be ignored
193 +*.[Cc]ache
194 +# but keep track of directories ending in .cache
195 +!*.[Cc]ache/
196 +
197 +# Others
198 +ClientBin/
199 +~$*
200 +*~
201 +*.dbmdl
202 +*.dbproj.schemaview
203 +*.jfm
204 +*.pfx
205 +*.publishsettings
206 +orleans.codegen.cs
207 +
208 +# Since there are multiple workflows, uncomment next line to ignore bower_components
209 +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
210 +#bower_components/
211 +
212 +# RIA/Silverlight projects
213 +Generated_Code/
214 +
215 +# Backup & report files from converting an old project file
216 +# to a newer Visual Studio version. Backup files are not needed,
217 +# because we have git ;-)
218 +_UpgradeReport_Files/
219 +Backup*/
220 +UpgradeLog*.XML
221 +UpgradeLog*.htm
222 +
223 +# SQL Server files
224 +*.mdf
225 +*.ldf
226 +*.ndf
227 +
228 +# Business Intelligence projects
229 +*.rdl.data
230 +*.bim.layout
231 +*.bim_*.settings
232 +
233 +# Microsoft Fakes
234 +FakesAssemblies/
235 +
236 +# GhostDoc plugin setting file
237 +*.GhostDoc.xml
238 +
239 +# Node.js Tools for Visual Studio
240 +.ntvs_analysis.dat
241 +node_modules/
242 +
243 +# Typescript v1 declaration files
244 +typings/
245 +
246 +# Visual Studio 6 build log
247 +*.plg
248 +
249 +# Visual Studio 6 workspace options file
250 +*.opt
251 +
252 +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
253 +*.vbw
254 +
255 +# Visual Studio LightSwitch build output
256 +**/*.HTMLClient/GeneratedArtifacts
257 +**/*.DesktopClient/GeneratedArtifacts
258 +**/*.DesktopClient/ModelManifest.xml
259 +**/*.Server/GeneratedArtifacts
260 +**/*.Server/ModelManifest.xml
261 +_Pvt_Extensions
262 +
263 +# Paket dependency manager
264 +.paket/paket.exe
265 +paket-files/
266 +
267 +# FAKE - F# Make
268 +.fake/
269 +
270 +# JetBrains Rider
271 +.idea/
272 +*.sln.iml
273 +
274 +# CodeRush
275 +.cr/
276 +
277 +# Python Tools for Visual Studio (PTVS)
278 +__pycache__/
279 +*.pyc
280 +
281 +# Cake - Uncomment if you are using it
282 +# tools/**
283 +# !tools/packages.config
284 +
285 +# Tabs Studio
286 +*.tss
287 +
288 +# Telerik's JustMock configuration file
289 +*.jmconfig
290 +
291 +# BizTalk build output
292 +*.btp.cs
293 +*.btm.cs
294 +*.odx.cs
295 +*.xsd.cs
\ No newline at end of file
LICENSE.TXT new
+28
@@ -0,0 +1,28 @@
1 +Copyright (c) .NET Foundation and contributors.
2 +This software is released under the Microsoft Reciprocal License (MS-RL) (the "License"); you may not use the software except in compliance with the License.
3 +
4 +The text of the Microsoft Reciprocal License (MS-RL) can be found online at:
5 + http://opensource.org/licenses/ms-rl
6 +
7 +
8 +Microsoft Reciprocal License (MS-RL)
9 +
10 +This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software.
11 +
12 +1. Definitions
13 + The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law.
14 + A "contribution" is the original software, or any additions or changes to the software.
15 + A "contributor" is any person that distributes its contribution under this license.
16 + "Licensed patents" are a contributor's patent claims that read directly on its contribution.
17 +
18 +2. Grant of Rights
19 + (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
20 + (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software.
21 +
22 +3. Conditions and Limitations
23 + (A) Reciprocal Grants- For any file you distribute that contains code from the software (in source code or binary format), you must provide recipients the source code to that file along with a copy of this license, which license will govern that file. You may license other files that are entirely your own work and do not contain code from the software under any terms you choose.
24 + (B) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
25 + (C) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically.
26 + (D) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software.
27 + (E) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license.
28 + (F) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement.
appveyor.cmd new
+13
@@ -0,0 +1,13 @@
1 +@setlocal
2 +@pushd %~dp0
3 +
4 +msbuild -p:Configuration=Release;Platform=x86;PlatformToolset=v140_xp
5 +msbuild -p:Configuration=Release;Platform=x64;PlatformToolset=v140_xp
6 +
7 +msbuild -p:Configuration=Release;Platform=x86;PlatformToolset=v141_xp
8 +msbuild -p:Configuration=Release;Platform=x64;PlatformToolset=v141_xp
9 +
10 +msbuild -p:Configuration=Release -t:PackNativeNuget src\wcautil\wcautil.vcxproj
11 +
12 +@popd
13 +@endlocal
\ No newline at end of file
appveyor.yml new
+27
@@ -0,0 +1,27 @@
1 +image: Visual Studio 2017
2 +
3 +version: 0.0.0.{build}
4 +configuration: Release
5 +
6 +environment:
7 + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
8 + DOTNET_CLI_TELEMETRY_OPTOUT: 1
9 + NUGET_XMLDOC_MODE: skip
10 +
11 +before_build:
12 + - nuget restore
13 +
14 +build_script:
15 + - appveyor.cmd
16 +
17 +pull_requests:
18 + do_not_increment_build_number: true
19 +
20 +nuget:
21 + disable_publish_on_pr: true
22 +
23 +skip_tags: true
24 +
25 +artifacts:
26 +- path: build\Release\**\*.nupkg
27 + name: nuget
nuget.config new
+8
@@ -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
src/Cpp.Build.props new
+100
@@ -0,0 +1,100 @@
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 + <BaseOutputPath>$(OutputPath)</BaseOutputPath>
7 + <IntDir>$(BaseIntermediateOutputPath)$(Platform)\</IntDir>
8 + <OutDir>$(OutputPath)$(Platform)\</OutDir>
9 + </PropertyGroup>
10 +
11 + <ItemDefinitionGroup>
12 + <ClCompile>
13 + <DisableSpecificWarnings>$(DisableSpecificCompilerWarnings)</DisableSpecificWarnings>
14 + <WarningLevel>Level4</WarningLevel>
15 + <AdditionalIncludeDirectories>$(ProjectDir)inc;$(MSBuildProjectDirectory);$(IntDir);$(SqlCESdkIncludePath);$(ProjectAdditionalIncludeDirectories);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
16 + <PreprocessorDefinitions>WIN32;_WINDOWS;_WIN32_MSI=500;_WIN32_WINNT=0x0501;$(ArmPreprocessorDefinitions);$(UnicodePreprocessorDefinitions);_CRT_STDIO_LEGACY_WIDE_SPECIFIERS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
17 + <PrecompiledHeader>Use</PrecompiledHeader>
18 + <PrecompiledHeaderFile>precomp.h</PrecompiledHeaderFile>
19 + <CallingConvention>StdCall</CallingConvention>
20 + <TreatWarningAsError>true</TreatWarningAsError>
21 + <ExceptionHandling>false</ExceptionHandling>
22 + <AdditionalOptions>-YlprecompDefine</AdditionalOptions>
23 + <AdditionalOptions Condition=" $(PlatformToolset.StartsWith('v14')) ">/Zc:threadSafeInit- %(AdditionalOptions)</AdditionalOptions>
24 + <MultiProcessorCompilation Condition=" $(NUMBER_OF_PROCESSORS) &gt; 4 ">true</MultiProcessorCompilation>
25 + </ClCompile>
26 + <ResourceCompile>
27 + <PreprocessorDefinitions>$(ArmPreprocessorDefinitions);%(PreprocessorDefinitions)</PreprocessorDefinitions>
28 + <AdditionalIncludeDirectories>$(ProjectAdditionalResourceIncludeDirectories);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
29 + </ResourceCompile>
30 + <Lib>
31 + <AdditionalLibraryDirectories>$(OutDir);$(AdditionalMultiTargetLibraryPath);$(ProjectAdditionalLibraryDirectories);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
32 + </Lib>
33 + <Link>
34 + <SubSystem>$(ProjectSubSystem)</SubSystem>
35 + <ModuleDefinitionFile>$(ProjectModuleDefinitionFile)</ModuleDefinitionFile>
36 + <NoEntryPoint>$(ResourceOnlyDll)</NoEntryPoint>
37 + <GenerateDebugInformation>true</GenerateDebugInformation>
38 + <AdditionalDependencies>$(ProjectAdditionalLinkLibraries);advapi32.lib;comdlg32.lib;user32.lib;oleaut32.lib;gdi32.lib;shell32.lib;ole32.lib;version.lib;%(AdditionalDependencies)</AdditionalDependencies>
39 + <AdditionalLibraryDirectories>$(OutDir);$(AdditionalMultiTargetLibraryPath);$(ArmLibraryDirectories);$(ProjectAdditionalLinkLibraryDirectories);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
40 + <AdditionalOptions Condition=" $(PlatformToolset.StartsWith('v14')) ">/IGNORE:4099 %(AdditionalOptions)</AdditionalOptions>
41 + </Link>
42 + </ItemDefinitionGroup>
43 +
44 + <ItemDefinitionGroup Condition=" '$(Platform)'=='Win32' and '$(PlatformToolset)'!='v100'">
45 + <ClCompile>
46 + <EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
47 + </ClCompile>
48 + </ItemDefinitionGroup>
49 + <ItemDefinitionGroup Condition=" '$(Platform)'=='arm' ">
50 + <ClCompile>
51 + <CallingConvention>CDecl</CallingConvention>
52 + </ClCompile>
53 + </ItemDefinitionGroup>
54 + <ItemDefinitionGroup Condition=" '$(ConfigurationType)'=='StaticLibrary' ">
55 + <ClCompile>
56 + <DebugInformationFormat>OldStyle</DebugInformationFormat>
57 + <OmitDefaultLibName>true</OmitDefaultLibName>
58 + <IgnoreAllDefaultLibraries>true</IgnoreAllDefaultLibraries>
59 + </ClCompile>
60 + </ItemDefinitionGroup>
61 + <ItemDefinitionGroup Condition=" '$(Configuration)'=='Debug' ">
62 + <ClCompile>
63 + <Optimization>Disabled</Optimization>
64 + <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
65 + <PreprocessorDefinitions>_DEBUG;DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
66 + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
67 + </ClCompile>
68 + </ItemDefinitionGroup>
69 + <ItemDefinitionGroup Condition=" '$(Configuration)'=='Debug' and '$(CLRSupport)'=='true' ">
70 + <ClCompile>
71 + <BasicRuntimeChecks></BasicRuntimeChecks>
72 + <RuntimeLibrary>MultiThreadedDebugDll</RuntimeLibrary>
73 + </ClCompile>
74 + </ItemDefinitionGroup>
75 + <ItemDefinitionGroup Condition=" '$(Configuration)'=='Release' ">
76 + <ClCompile>
77 + <Optimization>MinSpace</Optimization>
78 + <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
79 + <FunctionLevelLinking>true</FunctionLevelLinking>
80 + <IntrinsicFunctions>true</IntrinsicFunctions>
81 + <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
82 + </ClCompile>
83 + <Link>
84 + <EnableCOMDATFolding>true</EnableCOMDATFolding>
85 + <OptimizeReferences>true</OptimizeReferences>
86 + </Link>
87 + </ItemDefinitionGroup>
88 + <ItemDefinitionGroup Condition=" '$(Configuration)'=='Release' and '$(CLRSupport)'=='true' ">
89 + <ClCompile>
90 + <BasicRuntimeChecks></BasicRuntimeChecks>
91 + <RuntimeLibrary>MultiThreadedDll</RuntimeLibrary>
92 + </ClCompile>
93 + </ItemDefinitionGroup>
94 + <ItemDefinitionGroup Condition=" '$(CLRSupport)'=='true' ">
95 + <Link>
96 + <KeyFile>$(LinkKeyFile)</KeyFile>
97 + <DelaySign>$(LinkDelaySign)</DelaySign>
98 + </Link>
99 + </ItemDefinitionGroup>
100 +</Project>
src/Directory.Build.props new
+21
@@ -0,0 +1,21 @@
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 + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
7 + <BaseIntermediateOutputPath>$(MSBuildThisFileDirectory)..\build\obj\$(MSBuildProjectName)\</BaseIntermediateOutputPath>
8 + <OutputPath>$(MSBuildThisFileDirectory)..\build\$(Configuration)\</OutputPath>
9 +
10 + <Authors>WiX Toolset Team</Authors>
11 + <Company>WiX Toolset</Company>
12 + <Copyright>Copyright (c) .NET Foundation and contributors. All rights reserved.</Copyright>
13 + </PropertyGroup>
14 +
15 + <PropertyGroup>
16 + <WixToolsetRootFolder>$(MSBuildThisFileDirectory)..\..\</WixToolsetRootFolder>
17 + </PropertyGroup>
18 +
19 + <Import Project="Cpp.Build.props" Condition=" '$(MSBuildProjectExtension)'=='.vcxproj' " />
20 + <Import Project="Custom.Build.props" Condition=" Exists('Custom.Build.props') " />
21 +</Project>
src/NativeMultiTargeting.Build.props new
+10
@@ -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)$(PlatformToolset)\$(PlatformTarget)\</IntDir>
8 + <OutDir>$(OutputPath)$(PlatformToolset)\$(PlatformTarget)\</OutDir>
9 + </PropertyGroup>
10 +</Project>
src/wcautil/build/WixToolset.WcaUtil.props new
+23
@@ -0,0 +1,23 @@
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\lib\v140\$(PlatformTarget)\wcautil.lib;%(AdditionalDependencies)</AdditionalDependencies>
16 + </Link>
17 + </ItemDefinitionGroup>
18 + <ItemDefinitionGroup Condition=" $(PlatformToolset.ToLower().StartsWith('v141')) ">
19 + <Link>
20 + <AdditionalDependencies>$(MSBuildThisFileDirectory)native\lib\v141\$(PlatformTarget)\wcautil.lib;%(AdditionalDependencies)</AdditionalDependencies>
21 + </Link>
22 + </ItemDefinitionGroup>
23 +</Project>
src/wcautil/custommsierrors.h new
+130
@@ -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
src/wcautil/exbinary.cpp new
+142
@@ -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 +//
8 +HRESULT 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 +
45 +LExit:
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 +//
54 +HRESULT 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 +
81 +LExit:
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 +//
91 +HRESULT 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 +
138 +LExit:
139 + ReleaseMem(pbData);
140 +
141 + return hr;
142 +}
src/wcautil/inc/wcalog.h new
+14
@@ -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
6 +extern "C" {
7 +#endif
8 +
9 +BOOL WIXAPI IsVerboseLogging();
10 +HRESULT WIXAPI SetVerboseLoggingAtom(BOOL bValue);
11 +
12 +#ifdef __cplusplus
13 +}
14 +#endif
src/wcautil/inc/wcautil.h new
+384
@@ -0,0 +1,384 @@
1 +#pragma once
2 +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3 +
4 +
5 +#ifdef __cplusplus
6 +extern "C" {
7 +#endif
8 +
9 +#define WIXAPI __stdcall
10 +#define ExitTrace WcaLogError
11 +
12 +#include "dutil.h"
13 +
14 +#define MessageExitOnLastError(x, e, s, ...) { x = ::GetLastError(); x = HRESULT_FROM_WIN32(x); if (FAILED(x)) { ExitTrace(x, "%s", s, __VA_ARGS__); WcaErrorMessage(e, x, MB_OK, -1, __VA_ARGS__); goto LExit; } }
15 +#define MessageExitOnFailure(x, e, s, ...) if (FAILED(x)) { ExitTrace(x, "%s", s, __VA_ARGS__); WcaErrorMessage(e, x, INSTALLMESSAGE_ERROR | MB_OK, -1, __VA_ARGS__); goto LExit; }
16 +#define MessageExitOnNullWithLastError(p, x, e, s, ...) if (NULL == p) { x = ::GetLastError(); x = HRESULT_FROM_WIN32(x); if (!FAILED(x)) { x = E_FAIL; } ExitTrace(x, "%s", s, __VA_ARGS__); WcaErrorMessage(e, x, MB_OK, -1, __VA_ARGS__); goto LExit; }
17 +
18 +// Generic action enum.
19 +typedef enum WCA_ACTION
20 +{
21 + WCA_ACTION_NONE,
22 + WCA_ACTION_INSTALL,
23 + WCA_ACTION_UNINSTALL,
24 +} WCA_ACTION;
25 +
26 +typedef enum WCA_CASCRIPT
27 +{
28 + WCA_CASCRIPT_SCHEDULED,
29 + WCA_CASCRIPT_ROLLBACK,
30 +} WCA_CASCRIPT;
31 +
32 +typedef enum WCA_CASCRIPT_CLOSE
33 +{
34 + WCA_CASCRIPT_CLOSE_PRESERVE,
35 + WCA_CASCRIPT_CLOSE_DELETE,
36 +} WCA_CASCRIPT_CLOSE;
37 +
38 +typedef enum WCA_TODO
39 +{
40 + WCA_TODO_UNKNOWN,
41 + WCA_TODO_INSTALL,
42 + WCA_TODO_UNINSTALL,
43 + WCA_TODO_REINSTALL,
44 +} WCA_TODO;
45 +
46 +typedef struct WCA_CASCRIPT_STRUCT
47 +{
48 + LPWSTR pwzScriptPath;
49 + HANDLE hScriptFile;
50 +} *WCA_CASCRIPT_HANDLE;
51 +
52 +typedef enum WCA_ENCODING
53 +{
54 + WCA_ENCODING_UNKNOWN,
55 + WCA_ENCODING_UTF_16,
56 + WCA_ENCODING_UTF_8,
57 + WCA_ENCODING_ANSI,
58 +} WCA_ENCODING;
59 +
60 +void WIXAPI WcaGlobalInitialize(
61 + __in HINSTANCE hInst
62 + );
63 +void WIXAPI WcaGlobalFinalize();
64 +
65 +HRESULT WIXAPI WcaInitialize(
66 + __in MSIHANDLE hInstall,
67 + __in_z PCSTR szCustomActionLogName
68 + );
69 +UINT WIXAPI WcaFinalize(
70 + __in UINT iReturnValue
71 + );
72 +BOOL WIXAPI WcaIsInitialized();
73 +
74 +MSIHANDLE WIXAPI WcaGetInstallHandle();
75 +MSIHANDLE WIXAPI WcaGetDatabaseHandle();
76 +
77 +const char* WIXAPI WcaGetLogName();
78 +
79 +void WIXAPI WcaSetReturnValue(
80 + __in UINT iReturnValue
81 + );
82 +BOOL WIXAPI WcaCancelDetected();
83 +
84 +#define LOG_BUFFER 2048
85 +typedef enum LOGLEVEL
86 +{
87 + LOGMSG_TRACEONLY, // Never written to the log file (except in DEBUG builds)
88 + LOGMSG_VERBOSE, // Written to log when LOGVERBOSE
89 + LOGMSG_STANDARD // Written to log whenever informational logging is enabled
90 +} LOGLEVEL;
91 +
92 +void __cdecl WcaLog(
93 + __in LOGLEVEL llv,
94 + __in_z __format_string PCSTR fmt, ...
95 + );
96 +BOOL WIXAPI WcaDisplayAssert(
97 + __in LPCSTR sz
98 + );
99 +void __cdecl WcaLogError(
100 + __in HRESULT hr,
101 + __in LPCSTR szMessage,
102 + ...
103 + );
104 +
105 +UINT WIXAPI WcaProcessMessage(
106 + __in INSTALLMESSAGE eMessageType,
107 + __in MSIHANDLE hRecord
108 + );
109 +UINT __cdecl WcaErrorMessage(
110 + __in int iError,
111 + __in HRESULT hrError,
112 + __in UINT uiType,
113 + __in INT cArgs,
114 + ...
115 + );
116 +HRESULT WIXAPI WcaProgressMessage(
117 + __in UINT uiCost,
118 + __in BOOL fExtendProgressBar
119 + );
120 +
121 +BOOL WIXAPI WcaIsInstalling(
122 + __in INSTALLSTATE isInstalled,
123 + __in INSTALLSTATE isAction
124 + );
125 +BOOL WIXAPI WcaIsReInstalling(
126 + __in INSTALLSTATE isInstalled,
127 + __in INSTALLSTATE isAction
128 + );
129 +BOOL WIXAPI WcaIsUninstalling(
130 + __in INSTALLSTATE isInstalled,
131 + __in INSTALLSTATE isAction
132 + );
133 +
134 +HRESULT WIXAPI WcaSetComponentState(
135 + __in_z LPCWSTR wzComponent,
136 + __in INSTALLSTATE isState
137 + );
138 +
139 +HRESULT WIXAPI WcaTableExists(
140 + __in_z LPCWSTR wzTable
141 + );
142 +
143 +HRESULT WIXAPI WcaOpenView(
144 + __in_z LPCWSTR wzSql,
145 + __out MSIHANDLE* phView
146 + );
147 +HRESULT WIXAPI WcaExecuteView(
148 + __in MSIHANDLE hView,
149 + __in MSIHANDLE hRec
150 + );
151 +HRESULT WIXAPI WcaOpenExecuteView(
152 + __in_z LPCWSTR wzSql,
153 + __out MSIHANDLE* phView
154 + );
155 +HRESULT WIXAPI WcaFetchRecord(
156 + __in MSIHANDLE hView,
157 + __out MSIHANDLE* phRec
158 + );
159 +HRESULT WIXAPI WcaFetchSingleRecord(
160 + __in MSIHANDLE hView,
161 + __out MSIHANDLE* phRec
162 + );
163 +
164 +HRESULT WIXAPI WcaGetProperty(
165 + __in_z LPCWSTR wzProperty,
166 + __inout LPWSTR* ppwzData
167 + );
168 +HRESULT WIXAPI WcaGetFormattedProperty(
169 + __in_z LPCWSTR wzProperty,
170 + __out LPWSTR* ppwzData
171 + );
172 +HRESULT WIXAPI WcaGetFormattedString(
173 + __in_z LPCWSTR wzString,
174 + __out LPWSTR* ppwzData
175 + );
176 +HRESULT WIXAPI WcaGetIntProperty(
177 + __in_z LPCWSTR wzProperty,
178 + __inout int* piData
179 + );
180 +HRESULT WIXAPI WcaGetTargetPath(
181 + __in_z LPCWSTR wzFolder,
182 + __out LPWSTR* ppwzData
183 + );
184 +HRESULT WIXAPI WcaSetProperty(
185 + __in_z LPCWSTR wzPropertyName,
186 + __in_z LPCWSTR wzPropertyValue
187 + );
188 +HRESULT WIXAPI WcaSetIntProperty(
189 + __in_z LPCWSTR wzPropertyName,
190 + __in int nPropertyValue
191 + );
192 +BOOL WIXAPI WcaIsPropertySet(
193 + __in LPCSTR szProperty
194 + );
195 +BOOL WIXAPI WcaIsUnicodePropertySet(
196 + __in LPCWSTR wzProperty
197 + );
198 +
199 +HRESULT WIXAPI WcaGetRecordInteger(
200 + __in MSIHANDLE hRec,
201 + __in UINT uiField,
202 + __inout int* piData
203 + );
204 +HRESULT WIXAPI WcaGetRecordString(
205 + __in MSIHANDLE hRec,
206 + __in UINT uiField,
207 + __inout LPWSTR* ppwzData
208 + );
209 +HRESULT WIXAPI WcaGetRecordFormattedInteger(
210 + __in MSIHANDLE hRec,
211 + __in UINT uiField,
212 + __out int* piData
213 + );
214 +HRESULT WIXAPI WcaGetRecordFormattedString(
215 + __in MSIHANDLE hRec,
216 + __in UINT uiField,
217 + __inout LPWSTR* ppwzData
218 + );
219 +
220 +HRESULT WIXAPI WcaAllocStream(
221 + __deref_out_bcount_part(cbData, 0) BYTE** ppbData,
222 + __in DWORD cbData
223 + );
224 +HRESULT WIXAPI WcaFreeStream(
225 + __in BYTE* pbData
226 + );
227 +
228 +HRESULT WIXAPI WcaGetRecordStream(
229 + __in MSIHANDLE hRecBinary,
230 + __in UINT uiField,
231 + __deref_out_bcount_full(*pcbData) BYTE** ppbData,
232 + __out DWORD* pcbData
233 + );
234 +HRESULT WIXAPI WcaSetRecordString(
235 + __in MSIHANDLE hRec,
236 + __in UINT uiField,
237 + __in_z LPCWSTR wzData
238 + );
239 +HRESULT WIXAPI WcaSetRecordInteger(
240 + __in MSIHANDLE hRec,
241 + __in UINT uiField,
242 + __in int iValue
243 + );
244 +
245 +HRESULT WIXAPI WcaDoDeferredAction(
246 + __in_z LPCWSTR wzAction,
247 + __in_z LPCWSTR wzCustomActionData,
248 + __in UINT uiCost
249 + );
250 +DWORD WIXAPI WcaCountOfCustomActionDataRecords(
251 + __in_z LPCWSTR wzData
252 + );
253 +
254 +HRESULT WIXAPI WcaReadStringFromCaData(
255 + __deref_in LPWSTR* ppwzCustomActionData,
256 + __deref_out_z LPWSTR* ppwzString
257 + );
258 +HRESULT WIXAPI WcaReadIntegerFromCaData(
259 + __deref_in LPWSTR* ppwzCustomActionData,
260 + __out int* piResult
261 + );
262 +HRESULT WIXAPI WcaReadStreamFromCaData(
263 + __deref_in LPWSTR* ppwzCustomActionData,
264 + __deref_out_bcount(*pcbData) BYTE** ppbData,
265 + __out DWORD_PTR* pcbData
266 + );
267 +HRESULT WIXAPI WcaWriteStringToCaData(
268 + __in_z LPCWSTR wzString,
269 + __deref_inout_z LPWSTR* ppwzCustomActionData
270 + );
271 +HRESULT WIXAPI WcaWriteIntegerToCaData(
272 + __in int i,
273 + __deref_out_z_opt LPWSTR* ppwzCustomActionData
274 + );
275 +HRESULT WIXAPI WcaWriteStreamToCaData(
276 + __in_bcount(cbData) const BYTE* pbData,
277 + __in DWORD cbData,
278 + __deref_inout_z_opt LPWSTR* ppwzCustomActionData
279 + );
280 +
281 +HRESULT __cdecl WcaAddTempRecord(
282 + __inout MSIHANDLE* phTableView,
283 + __inout MSIHANDLE* phColumns,
284 + __in_z LPCWSTR wzTable,
285 + __out_opt MSIDBERROR* pdbError,
286 + __in UINT uiUniquifyColumn,
287 + __in UINT cColumns,
288 + ...
289 + );
290 +
291 +HRESULT WIXAPI WcaDumpTable(
292 + __in_z LPCWSTR wzTable
293 + );
294 +
295 +HRESULT WIXAPI WcaDeferredActionRequiresReboot();
296 +BOOL WIXAPI WcaDidDeferredActionRequireReboot();
297 +
298 +HRESULT WIXAPI WcaCaScriptCreateKey(
299 + __out LPWSTR* ppwzScriptKey
300 + );
301 +
302 +HRESULT WIXAPI WcaCaScriptCreate(
303 + __in WCA_ACTION action,
304 + __in WCA_CASCRIPT script,
305 + __in BOOL fImpersonated,
306 + __in_z LPCWSTR wzScriptKey,
307 + __in BOOL fAppend,
308 + __out WCA_CASCRIPT_HANDLE* phScript
309 + );
310 +
311 +HRESULT WIXAPI WcaCaScriptOpen(
312 + __in WCA_ACTION action,
313 + __in WCA_CASCRIPT script,
314 + __in BOOL fImpersonated,
315 + __in_z LPCWSTR wzScriptKey,
316 + __out WCA_CASCRIPT_HANDLE* phScript
317 + );
318 +
319 +void WIXAPI WcaCaScriptClose(
320 + __in_opt WCA_CASCRIPT_HANDLE hScript,
321 + __in WCA_CASCRIPT_CLOSE closeOperation
322 + );
323 +
324 +HRESULT WIXAPI WcaCaScriptReadAsCustomActionData(
325 + __in WCA_CASCRIPT_HANDLE hScript,
326 + __out LPWSTR* ppwzCustomActionData
327 + );
328 +
329 +HRESULT WIXAPI WcaCaScriptWriteString(
330 + __in WCA_CASCRIPT_HANDLE hScript,
331 + __in_z LPCWSTR wzValue
332 + );
333 +
334 +HRESULT WIXAPI WcaCaScriptWriteNumber(
335 + __in WCA_CASCRIPT_HANDLE hScript,
336 + __in DWORD dwValue
337 + );
338 +
339 +void WIXAPI WcaCaScriptFlush(
340 + __in WCA_CASCRIPT_HANDLE hScript
341 + );
342 +
343 +void WIXAPI WcaCaScriptCleanup(
344 + __in_z LPCWSTR wzProductCode,
345 + __in BOOL fImpersonated
346 + );
347 +
348 +HRESULT WIXAPI QuietExec(
349 + __inout_z LPWSTR wzCommand,
350 + __in DWORD dwTimeout,
351 + __in BOOL fLogCommand,
352 + __in BOOL fLogOutput
353 + );
354 +
355 +HRESULT WIXAPI QuietExecCapture(
356 + __inout_z LPWSTR wzCommand,
357 + __in DWORD dwTimeout,
358 + __in BOOL fLogCommand,
359 + __in BOOL fLogOutput,
360 + __out_z_opt LPWSTR* psczOutput
361 + );
362 +
363 +WCA_TODO WIXAPI WcaGetComponentToDo(
364 + __in_z LPCWSTR wzComponentId
365 + );
366 +
367 +HRESULT WIXAPI WcaExtractBinaryToBuffer(
368 + __in LPCWSTR wzBinaryId,
369 + __out BYTE** pbData,
370 + __out DWORD* pcbData
371 + );
372 +HRESULT WIXAPI WcaExtractBinaryToFile(
373 + __in LPCWSTR wzBinaryId,
374 + __in LPCWSTR wzPath
375 + );
376 +HRESULT WIXAPI WcaExtractBinaryToString(
377 + __in LPCWSTR wzBinaryId,
378 + __deref_out_z LPWSTR* psczOutput,
379 + __out WCA_ENCODING* encoding
380 + );
381 +
382 +#ifdef __cplusplus
383 +}
384 +#endif
src/wcautil/inc/wcawow64.h new
+20
@@ -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
8 +extern "C" {
9 +#endif
10 +
11 +HRESULT WIXAPI WcaInitializeWow64();
12 +BOOL WIXAPI WcaIsWow64Process();
13 +BOOL WIXAPI WcaIsWow64Initialized();
14 +HRESULT WIXAPI WcaDisableWow64FSRedirection();
15 +HRESULT WIXAPI WcaRevertWow64FSRedirection();
16 +HRESULT WIXAPI WcaFinalizeWow64();
17 +
18 +#ifdef __cplusplus
19 +}
20 +#endif
src/wcautil/inc/wcawrapquery.h new
+130
@@ -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
8 +typedef enum eWrapQueryAction
9 +{
10 + wqaTableBegin = 1,
11 + wqaTableFinish,
12 + wqaRowBegin,
13 + wqaRowFinish
14 +} eWrapQueryAction;
15 +
16 +typedef enum eColumnDataType
17 +{
18 + cdtString = 1,
19 + cdtInt,
20 + cdtStream,
21 + cdtUnknown
22 +} eColumnDataType;
23 +
24 +typedef 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)
61 +typedef 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
82 +HRESULT 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
90 +HRESULT WIXAPI WcaWrapEmptyQuery(
91 + __inout LPWSTR * ppwzCustomActionData
92 + );
93 +
94 +// Open a new unwrap query operation, with data from the ppwzCustomActionData string
95 +HRESULT 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
101 +DWORD 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
106 +void 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!
112 +HRESULT 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!
120 +HRESULT 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)
128 +void WIXAPI WcaFinishUnwrapQuery(
129 + __in_opt WCA_WRAPQUERY_HANDLE hWrapQuery
130 + );
src/wcautil/packages.config new
+5
@@ -0,0 +1,5 @@
1 +<?xml version="1.0" encoding="utf-8"?>
2 +<packages>
3 + <package id="Nerdbank.GitVersioning" version="2.0.37-beta" targetFramework="native" developmentDependency="true" />
4 + <package id="WixToolset.DUtil" version="4.0.3" targetFramework="native" />
5 +</packages>
\ No newline at end of file
src/wcautil/precomp.h new
+19
@@ -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 +
10 +const 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"
src/wcautil/qtexec.cpp new
+340
@@ -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 +
8 +static 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 +
86 +LExit:
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 +
98 +static 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 +
227 +LExit:
228 + ReleaseMem(pBuffer);
229 +
230 + ReleaseStr(szLog);
231 + ReleaseStr(szTemp);
232 + ReleaseStr(szWrite);
233 + ReleaseStr(sczEscaped);
234 +
235 + return hr;
236 +}
237 +
238 +static 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 +
316 +LExit:
317 + return hr;
318 +}
319 +
320 +
321 +HRESULT 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 +
331 +HRESULT 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 +}
src/wcautil/wcalog.cpp new
+251
@@ -0,0 +1,251 @@
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 +********************************************************************/
10 +static 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 +********************************************************************/
44 +BOOL 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 + int cchMsiLogging = lstrlenW(pwzMsiLogging);
58 + if (0 < cchMsiLogging)
59 + {
60 + for (int i = 0; i < cchMsiLogging; 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 +
78 +LExit:
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 +********************************************************************/
94 +HRESULT 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 +
125 +LExit:
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 +********************************************************************/
137 +static 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 +*******************************************************************/
158 +extern "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 +********************************************************************/
221 +extern "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 exists the function
232 +
233 + NOTE: writes the hresult and error string to the MSI log
234 +********************************************************************/
235 +extern "C" void WcaLogError(
236 + __in HRESULT hr,
237 + __in LPCSTR szMessage,
238 + ...
239 + )
240 +{
241 + char szBuffer[LOG_BUFFER];
242 + va_list dots;
243 +
244 + va_start(dots, szMessage);
245 + StringCchVPrintfA(szBuffer, countof(szBuffer), szMessage, dots);
246 + va_end(dots);
247 +
248 + // log the message if using Wca common layer
249 + if (WcaIsInitialized())
250 + WcaLog(LOGMSG_STANDARD, "Error 0x%x: %s", hr, szBuffer);
251 +}
src/wcautil/wcascript.cpp new
+447
@@ -0,0 +1,447 @@
1 +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2 +
3 +#include "precomp.h"
4 +
5 +
6 +static HRESULT 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 +********************************************************************/
20 +extern "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 +
30 +LExit:
31 + return hr;
32 +}
33 +
34 +
35 +/********************************************************************
36 + WcaCaScriptCreate() - creates the appropriate script for this
37 + CustomAction Script Key.
38 +
39 +********************************************************************/
40 +extern "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 +
75 +LExit:
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 +********************************************************************/
91 +extern "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 +
120 +LExit:
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 +********************************************************************/
135 +extern "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 +********************************************************************/
168 +extern "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 +
233 +LExit:
234 + ReleaseMem(pbData);
235 + return hr;
236 +}
237 +
238 +
239 +/********************************************************************
240 + WcaCaScriptWriteString() - writes a string to the ca script.
241 +
242 +********************************************************************/
243 +extern "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 + DWORD cbWrite = 0;
251 + DWORD cbTotalWritten = 0;
252 + WCHAR delim[] = { MAGIC_MULTISZ_DELIM }; // magic char followed by NULL terminator
253 +
254 + cbFile = ::SetFilePointer(hScript->hScriptFile, 0, NULL, FILE_END);
255 + if (INVALID_SET_FILE_POINTER == cbFile)
256 + {
257 + ExitWithLastError(hr, "Failed to move file pointer to end of file.");
258 + }
259 +
260 + // If there is existing data in the file, append on the magic delimeter
261 + // before adding our new data on the end of the file.
262 + if (0 < cbFile)
263 + {
264 + cbWrite = sizeof(delim);
265 + cbTotalWritten = 0;
266 + while (cbTotalWritten < cbWrite)
267 + {
268 + DWORD cbWritten = 0;
269 + if (!::WriteFile(hScript->hScriptFile, reinterpret_cast<BYTE*>(delim) + cbTotalWritten, cbWrite - cbTotalWritten, &cbWritten, NULL))
270 + {
271 + ExitWithLastError(hr, "Failed to write data to ca script.");
272 + }
273 +
274 + cbTotalWritten += cbWritten;
275 + }
276 + }
277 +
278 + cbWrite = lstrlenW(wzValue) * sizeof(WCHAR);
279 + cbTotalWritten = 0;
280 + while (cbTotalWritten < cbWrite)
281 + {
282 + DWORD cbWritten = 0;
283 + if (!::WriteFile(hScript->hScriptFile, reinterpret_cast<const BYTE*>(wzValue) + cbTotalWritten, cbWrite - cbTotalWritten, &cbWritten, NULL))
284 + {
285 + ExitWithLastError(hr, "Failed to write data to ca script.");
286 + }
287 +
288 + cbTotalWritten += cbWritten;
289 + }
290 +
291 +LExit:
292 + return hr;
293 +}
294 +
295 +
296 +/********************************************************************
297 + WcaCaScriptWriteNumber() - writes a number to the ca script.
298 +
299 +********************************************************************/
300 +extern "C" HRESULT WIXAPI WcaCaScriptWriteNumber(
301 + __in WCA_CASCRIPT_HANDLE hScript,
302 + __in DWORD dwValue
303 + )
304 +{
305 + HRESULT hr = S_OK;
306 + WCHAR wzBuffer[13] = { 0 };
307 +
308 + hr = ::StringCchPrintfW(wzBuffer, countof(wzBuffer), L"%u", dwValue);
309 + ExitOnFailure(hr, "Failed to convert number into string.");
310 +
311 + hr = WcaCaScriptWriteString(hScript, wzBuffer);
312 + ExitOnFailure(hr, "Failed to write number to script.");
313 +
314 +LExit:
315 + return hr;
316 +}
317 +
318 +
319 +/********************************************************************
320 + WcaCaScriptFlush() - best effort function to get script written to
321 + disk.
322 +
323 +********************************************************************/
324 +extern "C" void WIXAPI WcaCaScriptFlush(
325 + __in WCA_CASCRIPT_HANDLE hScript
326 + )
327 +{
328 + ::FlushFileBuffers(hScript->hScriptFile);
329 +}
330 +
331 +
332 +/********************************************************************
333 + WcaCaScriptCleanup() - best effort clean-up of any cascripts left
334 + over from this install/uninstall.
335 +
336 +********************************************************************/
337 +extern "C" void WIXAPI WcaCaScriptCleanup(
338 + __in_z LPCWSTR wzProductCode,
339 + __in BOOL fImpersonated
340 + )
341 +{
342 + HRESULT hr = S_OK;
343 + WCHAR wzTempPath[MAX_PATH];
344 + LPWSTR pwzWildCardPath = NULL;
345 + WIN32_FIND_DATAW fd = { 0 };
346 + HANDLE hff = INVALID_HANDLE_VALUE;
347 + LPWSTR pwzDeletePath = NULL;
348 +
349 + if (fImpersonated)
350 + {
351 + if (!::GetTempPathW(countof(wzTempPath), wzTempPath))
352 + {
353 + ExitWithLastError(hr, "Failed to get temp path.");
354 + }
355 + }
356 + else
357 + {
358 + if (!::GetWindowsDirectoryW(wzTempPath, countof(wzTempPath)))
359 + {
360 + ExitWithLastError(hr, "Failed to get windows path.");
361 + }
362 +
363 + hr = ::StringCchCatW(wzTempPath, countof(wzTempPath), L"\\Installer\\");
364 + ExitOnFailure(hr, "Failed to concat Installer directory on windows path string.");
365 + }
366 +
367 + hr = StrAllocFormatted(&pwzWildCardPath, L"%swix%s.*.???", wzTempPath, wzProductCode);
368 + ExitOnFailure(hr, "Failed to allocate wildcard path to ca scripts.");
369 +
370 + hff = ::FindFirstFileW(pwzWildCardPath, &fd);
371 + if (INVALID_HANDLE_VALUE == hff)
372 + {
373 + ExitWithLastError(hr, "Failed to find files with pattern: %ls", pwzWildCardPath);
374 + }
375 +
376 + do
377 + {
378 + hr = StrAllocFormatted(&pwzDeletePath, L"%s%s", wzTempPath, fd.cFileName);
379 + if (SUCCEEDED(hr))
380 + {
381 + if (!::DeleteFileW(pwzDeletePath))
382 + {
383 + DWORD er = ::GetLastError();
384 + WcaLog(LOGMSG_VERBOSE, "Failed to clean up CAScript file: %ls, er: %d", fd.cFileName, er);
385 + }
386 + }
387 + else
388 + {
389 + WcaLog(LOGMSG_VERBOSE, "Failed to allocate path to clean up CAScript file: %ls, hr: 0x%x", fd.cFileName, hr);
390 + }
391 + } while(::FindNextFileW(hff, &fd));
392 +
393 +LExit:
394 + if (INVALID_HANDLE_VALUE == hff)
395 + {
396 + ::FindClose(hff);
397 + }
398 +
399 + ReleaseStr(pwzDeletePath);
400 + ReleaseStr(pwzWildCardPath);
401 + return;
402 +}
403 +
404 +
405 +static HRESULT CaScriptFileName(
406 + __in WCA_ACTION action,
407 + __in WCA_CASCRIPT script,
408 + __in BOOL fImpersonated,
409 + __in_z LPCWSTR wzScriptKey,
410 + __out LPWSTR* ppwzScriptName
411 + )
412 +{
413 + HRESULT hr = S_OK;
414 + WCHAR wzTempPath[MAX_PATH];
415 + LPWSTR pwzProductCode = NULL;
416 + WCHAR chInstallOrUninstall = action == WCA_ACTION_INSTALL ? L'i' : L'u';
417 + WCHAR chScheduledOrRollback = script == WCA_CASCRIPT_SCHEDULED ? L's' : L'r';
418 + WCHAR chUserOrMachine = fImpersonated ? L'u' : L'm';
419 +
420 + if (fImpersonated)
421 + {
422 + if (!::GetTempPathW(countof(wzTempPath), wzTempPath))
423 + {
424 + ExitWithLastError(hr, "Failed to get temp path.");
425 + }
426 + }
427 + else
428 + {
429 + if (!::GetWindowsDirectoryW(wzTempPath, countof(wzTempPath)))
430 + {
431 + ExitWithLastError(hr, "Failed to get windows path.");
432 + }
433 +
434 + hr = ::StringCchCatW(wzTempPath, countof(wzTempPath), L"\\Installer\\");
435 + ExitOnFailure(hr, "Failed to concat Installer directory on windows path string.");
436 + }
437 +
438 + hr = WcaGetProperty(L"ProductCode", &pwzProductCode);
439 + ExitOnFailure(hr, "Failed to get ProductCode.");
440 +
441 + hr = StrAllocFormatted(ppwzScriptName, L"%swix%s.%s.%c%c%c", wzTempPath, pwzProductCode, wzScriptKey, chScheduledOrRollback, chUserOrMachine, chInstallOrUninstall);
442 + ExitOnFailure(hr, "Failed to allocate path to ca script.");
443 +
444 +LExit:
445 + ReleaseStr(pwzProductCode);
446 + return hr;
447 +}
src/wcautil/wcautil.cpp new
+216
@@ -0,0 +1,216 @@
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
6 +HMODULE g_hInstCADLL;
7 +
8 +// statics
9 +static BOOL s_fInitialized;
10 +static MSIHANDLE s_hInstall;
11 +static MSIHANDLE s_hDatabase;
12 +static char s_szCustomActionLogName[32];
13 +static UINT s_iRetVal;
14 +
15 +
16 +/********************************************************************
17 + WcaGlobalInitialize() - initializes the Wca library, should be
18 + called once per custom action Dll during
19 + DllMain on DLL_PROCESS_ATTACH
20 +
21 +********************************************************************/
22 +extern "C" void WIXAPI WcaGlobalInitialize(
23 + __in HINSTANCE hInst
24 + )
25 +{
26 + g_hInstCADLL = hInst;
27 + MemInitialize();
28 +
29 + AssertSetModule(g_hInstCADLL);
30 + AssertSetDisplayFunction(WcaDisplayAssert);
31 +}
32 +
33 +
34 +/********************************************************************
35 + WcaGlobalFinalize() - finalizes the Wca library, should be the
36 + called once per custom action Dll during
37 + DllMain on DLL_PROCESS_DETACH
38 +
39 +********************************************************************/
40 +extern "C" void WIXAPI WcaGlobalFinalize()
41 +{
42 +#ifdef DEBUG
43 + if (WcaIsInitialized())
44 + {
45 + CHAR szBuf[2048];
46 + StringCchPrintfA(szBuf, countof(szBuf), "CustomAction %s called WcaInitialize() but not WcaFinalize()", WcaGetLogName());
47 +
48 + AssertSz(FALSE, szBuf);
49 + }
50 +#endif
51 + MemUninitialize();
52 + g_hInstCADLL = NULL;
53 +}
54 +
55 +
56 +/********************************************************************
57 + WcaInitialize() - initializes the Wca framework, should be the first
58 + thing called by all CustomActions
59 +
60 +********************************************************************/
61 +extern "C" HRESULT WIXAPI WcaInitialize(
62 + __in MSIHANDLE hInstall,
63 + __in_z PCSTR szCustomActionLogName
64 + )
65 +{
66 + WCHAR wzCAFileName[MAX_PATH] = {0};
67 + DWORD dwMajorVersion = 0;
68 + DWORD dwMinorVersion = 0;
69 +
70 + // these statics should be called once per CustomAction invocation.
71 + // Darwin does doesn't preserve DLL state across CustomAction calls so
72 + // these should always be initialized to NULL. If that behavior changes
73 + // we would need to do a careful review of all of our module/global data.
74 + AssertSz(!s_fInitialized, "WcaInitialize() should only be called once per CustomAction");
75 + Assert(NULL == s_hInstall);
76 + Assert(NULL == s_hDatabase);
77 + Assert(0 == *s_szCustomActionLogName);
78 +
79 + HRESULT hr = S_OK;
80 +
81 + s_fInitialized = TRUE;
82 + s_iRetVal = ERROR_SUCCESS; // assume all will go well
83 +
84 + s_hInstall = hInstall;
85 + s_hDatabase = ::MsiGetActiveDatabase(s_hInstall); // may return null if deferred CustomAction
86 +
87 + hr = ::StringCchCopy(s_szCustomActionLogName, countof(s_szCustomActionLogName), szCustomActionLogName);
88 + ExitOnFailure(hr, "Failed to copy CustomAction log name: %s", szCustomActionLogName);
89 +
90 + // If we got the database handle IE: immediate CA
91 + if (s_hDatabase)
92 + {
93 + hr = SetVerboseLoggingAtom(IsVerboseLogging());
94 + ExitOnFailure(hr, "Failed to set verbose logging global atom");
95 + }
96 +
97 + if (!::GetModuleFileNameW(g_hInstCADLL, wzCAFileName, countof(wzCAFileName)))
98 + {
99 + ExitWithLastError(hr, "Failed to get module filename");
100 + }
101 +
102 + FileVersion(wzCAFileName, &dwMajorVersion, &dwMinorVersion); // Ignore failure, just log 0.0.0.0
103 +
104 + 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));
105 +
106 + Assert(s_hInstall);
107 +LExit:
108 + if (FAILED(hr))
109 + {
110 + if (s_hDatabase)
111 + {
112 + ::MsiCloseHandle(s_hDatabase);
113 + s_hDatabase = NULL;
114 + }
115 +
116 + s_hInstall = NULL;
117 + s_fInitialized = FALSE;
118 + }
119 +
120 + return hr;
121 +}
122 +
123 +
124 +/********************************************************************
125 + WcaFinalize() - cleans up after the Wca framework, should be the last
126 + thing called by all CustomActions
127 +
128 +********************************************************************/
129 +extern "C" UINT WIXAPI WcaFinalize(
130 + __in UINT iReturnValue
131 + )
132 +{
133 + AssertSz(!WcaIsWow64Initialized(), "WcaFinalizeWow64() should be called before calling WcaFinalize()");
134 +
135 + // clean up after our initialization
136 + if (s_hDatabase)
137 + {
138 + ::MsiCloseHandle(s_hDatabase);
139 + s_hDatabase = NULL;
140 + }
141 +
142 + s_hInstall = NULL;
143 + s_fInitialized = FALSE;
144 +
145 + // if no error occurred during the processing of the CusotmAction return the passed in return value
146 + // otherwise return the previous failure
147 + return (ERROR_SUCCESS == s_iRetVal) ? iReturnValue : s_iRetVal;
148 +}
149 +
150 +
151 +/********************************************************************
152 + WcaIsInitialized() - determines if WcaInitialize() has been called
153 +
154 +********************************************************************/
155 +extern "C" BOOL WIXAPI WcaIsInitialized()
156 +{
157 + return s_fInitialized;
158 +}
159 +
160 +
161 +/********************************************************************
162 + WcaGetInstallHandle() - gets the handle to the active install session
163 +
164 +********************************************************************/
165 +extern "C" MSIHANDLE WIXAPI WcaGetInstallHandle()
166 +{
167 + AssertSz(s_hInstall, "WcaInitialize() should be called before attempting to access the install handle.");
168 + return s_hInstall;
169 +}
170 +
171 +
172 +/********************************************************************
173 + WcaGetDatabaseHandle() - gets the handle to the active database
174 +
175 + NOTE: this function can only be used in immediate CustomActions.
176 + Deferred CustomActions do not have access to the active
177 + database.
178 +********************************************************************/
179 +extern "C" MSIHANDLE WIXAPI WcaGetDatabaseHandle()
180 +{
181 + 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.");
182 + return s_hDatabase;
183 +}
184 +
185 +
186 +/********************************************************************
187 + WcaGetLogName() - gets the name of the CustomAction used in logging
188 +
189 +********************************************************************/
190 +extern "C" const char* WIXAPI WcaGetLogName()
191 +{
192 + return s_szCustomActionLogName;
193 +}
194 +
195 +
196 +/********************************************************************
197 + WcaSetReturnValue() - sets the value to return from the CustomAction
198 +
199 +********************************************************************/
200 +extern "C" void WIXAPI WcaSetReturnValue(
201 + __in UINT iReturnValue
202 + )
203 +{
204 + s_iRetVal = iReturnValue;
205 +}
206 +
207 +
208 +/********************************************************************
209 + WcaCancelDetected() - determines if the user has canceled yet
210 +
211 + NOTE: returns true when WcaSetReturnValue() is set to ERROR_INSTALL_USEREXIT
212 +********************************************************************/
213 +extern "C" BOOL WIXAPI WcaCancelDetected()
214 +{
215 + return ERROR_INSTALL_USEREXIT == s_iRetVal;
216 +}
src/wcautil/wcautil.nuspec new
+23
@@ -0,0 +1,23 @@
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 + <licenseUrl>https://github.com/wixtoolset/wcautil/blob/master/LICENSE.TXT</licenseUrl>
9 + <projectUrl>https://github.com/wixtoolset/wcautil</projectUrl>
10 + <requireLicenseAcceptance>false</requireLicenseAcceptance>
11 + <description>$description$</description>
12 + <copyright>$copyright$</copyright>
13 + </metadata>
14 +
15 + <files>
16 + <file src="build\$id$.props" target="build\" />
17 + <file src="inc\*" target="build\native\include" />
18 + <file src="..\..\build\$configuration$\v140_xp\x64\wcautil.lib" target="build\native\lib\v140\x64" />
19 + <file src="..\..\build\$configuration$\v140_xp\x86\wcautil.lib" target="build\native\lib\v140\x86" />
20 + <file src="..\..\build\$configuration$\v141_xp\x64\wcautil.lib" target="build\native\lib\v141\x64" />
21 + <file src="..\..\build\$configuration$\v141_xp\x86\wcautil.lib" target="build\native\lib\v141\x86" />
22 + </files>
23 +</package>
src/wcautil/wcautil.vcxproj new
+79
@@ -0,0 +1,79 @@
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.3\build\WixToolset.DUtil.props" Condition="Exists('..\..\packages\WixToolset.DUtil.4.0.3\build\WixToolset.DUtil.props')" />
5 + <ItemGroup Label="ProjectConfigurations">
6 + <ProjectConfiguration Include="Debug|Win32">
7 + <Configuration>Debug</Configuration>
8 + <Platform>Win32</Platform>
9 + </ProjectConfiguration>
10 + <ProjectConfiguration Include="Release|Win32">
11 + <Configuration>Release</Configuration>
12 + <Platform>Win32</Platform>
13 + </ProjectConfiguration>
14 + <ProjectConfiguration Include="Debug|x64">
15 + <Configuration>Debug</Configuration>
16 + <Platform>x64</Platform>
17 + </ProjectConfiguration>
18 + <ProjectConfiguration Include="Release|x64">
19 + <Configuration>Release</Configuration>
20 + <Platform>x64</Platform>
21 + </ProjectConfiguration>
22 + </ItemGroup>
23 + <PropertyGroup Label="Globals">
24 + <ProjectGuid>{5B3714B6-3A76-463E-8595-D48DA276C512}</ProjectGuid>
25 + <ConfigurationType>StaticLibrary</ConfigurationType>
26 + <TargetName>wcautil</TargetName>
27 + <MultiTargetLibrary>true</MultiTargetLibrary>
28 + <PlatformToolset>v141_xp</PlatformToolset>
29 + <CharacterSet>MultiByte</CharacterSet>
30 + <Description>WiX Toolset Custom Action native utility library</Description>
31 + </PropertyGroup>
32 + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
33 + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
34 + <ImportGroup Label="Shared">
35 + <Import Project="..\..\packages\Nerdbank.GitVersioning.2.0.37-beta\build\Nerdbank.GitVersioning.targets" Condition="Exists('..\..\packages\Nerdbank.GitVersioning.2.0.37-beta\build\Nerdbank.GitVersioning.targets')" />
36 + </ImportGroup>
37 + <Import Project="..\NativeMultiTargeting.Build.props" />
38 + <ItemGroup>
39 + <ClCompile Include="exbinary.cpp" />
40 + <ClCompile Include="wcalog.cpp" />
41 + <ClCompile Include="wcascript.cpp" />
42 + <ClCompile Include="wcautil.cpp">
43 + <PrecompiledHeader>Create</PrecompiledHeader>
44 + </ClCompile>
45 + <ClCompile Include="wcawrapquery.cpp" />
46 + <ClCompile Include="wcawow64.cpp" />
47 + <ClCompile Include="wcawrap.cpp" />
48 + <ClCompile Include="qtexec.cpp" />
49 + </ItemGroup>
50 + <ItemGroup Condition="'$(Platform)' == 'Win32'">
51 + <ClInclude Include="custommsierrors.h">
52 + <GenerateWixInclude>caerr.wxi</GenerateWixInclude>
53 + </ClInclude>
54 + </ItemGroup>
55 + <ItemGroup Condition="'$(Platform)' != 'Win32'">
56 + <ClInclude Include="custommsierrors.h" />
57 + </ItemGroup>
58 + <ItemGroup>
59 + <ClInclude Include="precomp.h" />
60 + <ClInclude Include="inc\wcalog.h" />
61 + <ClInclude Include="inc\wcautil.h" />
62 + <ClInclude Include="inc\wcawow64.h" />
63 + <ClInclude Include="inc\wcawrapquery.h" />
64 + </ItemGroup>
65 + <ItemGroup>
66 + <None Include="packages.config" />
67 + </ItemGroup>
68 + <Target Name="PackNativeNuget" DependsOnTargets="GetBuildVersion">
69 + <Exec Command="nuget pack wcautil.nuspec -OutputDirectory $(BaseOutputPath) -Properties Configuration=$(Configuration);Id=WixToolset.WcaUtil;Version=&quot;$(BuildVersionSimple)&quot;;Authors=&quot;$(Authors)&quot;;Copyright=&quot;$(Copyright)&quot;;Description=&quot;$(Description)&quot;;Title=&quot;$(Title)&quot;" />
70 + </Target>
71 + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
72 + <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
73 + <PropertyGroup>
74 + <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>
75 + </PropertyGroup>
76 + <Error Condition="!Exists('..\..\packages\Nerdbank.GitVersioning.2.0.37-beta\build\Nerdbank.GitVersioning.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Nerdbank.GitVersioning.2.0.37-beta\build\Nerdbank.GitVersioning.targets'))" />
77 + <Error Condition="!Exists('..\..\packages\WixToolset.DUtil.4.0.3\build\WixToolset.DUtil.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\WixToolset.DUtil.4.0.3\build\WixToolset.DUtil.props'))" />
78 + </Target>
79 +</Project>
\ No newline at end of file
src/wcautil/wcawow64.cpp new
+169
@@ -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 +
5 +static HMODULE s_hKernel32;
6 +static BOOL s_fWow64Initialized;
7 +static BOOL (*s_pfnDisableWow64)(__out PVOID* );
8 +static BOOL (*s_pfnRevertWow64)(__in PVOID );
9 +static BOOL (*s_pfnIsWow64Process) (HANDLE, PBOOL);
10 +static PVOID s_Wow64FSRevertState;
11 +static BOOL s_fWow64FSDisabled;
12 +
13 +/********************************************************************
14 + WcaInitializeWow64() - Initializes the Wow64 API
15 +
16 +********************************************************************/
17 +extern "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 +
61 +LExit:
62 +
63 + return hr;
64 +}
65 +
66 +/********************************************************************
67 + WcaIsWow64Process() - determines if the current process is running
68 + in WOW
69 +
70 +********************************************************************/
71 +extern "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 +********************************************************************/
90 +extern "C" BOOL WIXAPI WcaIsWow64Initialized()
91 +{
92 + return s_fWow64Initialized;
93 +}
94 +
95 +/********************************************************************
96 + WcaDisableWow64FSRedirection() - Disables Wow64 FS Redirection
97 +
98 +********************************************************************/
99 +extern "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 +
117 +LExit:
118 + return hr;
119 +}
120 +
121 +/********************************************************************
122 + WcaRevertWow64FSRedirection() - Reverts Wow64 FS Redirection to its
123 + pre-disabled state
124 +
125 +********************************************************************/
126 +extern "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 +
144 +LExit:
145 + return hr;
146 +}
147 +
148 +/********************************************************************
149 + WcaFinalizeWow64() - Cleans up after Wow64 API Initialization
150 +
151 +********************************************************************/
152 +extern "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 +}
src/wcautil/wcawrap.cpp new
+1643
@@ -0,0 +1,1643 @@
1 +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2 +
3 +#include "precomp.h"
4 +
5 +
6 +/********************************************************************
7 +WcaProcessMessage() - sends a message from the CustomAction
8 +
9 +********************************************************************/
10 +extern "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 +/********************************************************************
26 +WcaErrorMessage() - sends an error message from the CustomAction using
27 +the Error table
28 +
29 +NOTE: Any and all var_args (...) must be WCHAR*
30 + If you pass -1 to cArgs the count will be determined
31 +********************************************************************/
32 +extern "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);
79 +LExit:
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 +/********************************************************************
95 +WcaProgressMessage() - extends the progress bar or sends a progress
96 +update from the CustomAction
97 +
98 +********************************************************************/
99 +extern "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 +
167 +LExit:
168 + if (hRec)
169 + {
170 + ::MsiCloseHandle(hRec);
171 + }
172 +
173 + return hr;
174 +}
175 +
176 +
177 +/********************************************************************
178 +WcaIsInstalling() - determines if a pair of installstates means install
179 +
180 +********************************************************************/
181 +extern "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 +/********************************************************************
194 +WcaIsReInstalling() - determines if a pair of installstates means reinstall
195 +
196 +********************************************************************/
197 +extern "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 +/********************************************************************
211 +WcaIsUninstalling() - determines if a pair of installstates means uninstall
212 +
213 +********************************************************************/
214 +extern "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 +/********************************************************************
227 +WcaGetComponentToDo() - gets a component's install states and
228 +determines if they mean install, uninstall, or reinstall.
229 +********************************************************************/
230 +extern "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 +/********************************************************************
261 +WcaSetComponentState() - sets the install state of a Component
262 +
263 +********************************************************************/
264 +extern "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 +/********************************************************************
280 +WcaTableExists() - determines if installing database contains a table
281 +
282 +********************************************************************/
283 +extern "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 +/********************************************************************
317 +WcaOpenView() - opens a view on the installing database
318 +
319 +********************************************************************/
320 +extern "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 +
334 +LExit:
335 + return hr;
336 +}
337 +
338 +
339 +/********************************************************************
340 +WcaExecuteView() - executes a parameterized open view on the installing database
341 +
342 +********************************************************************/
343 +extern "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 +
358 +LExit:
359 + return hr;
360 +}
361 +
362 +
363 +/********************************************************************
364 +WcaOpenExecuteView() - opens and executes a view on the installing database
365 +
366 +********************************************************************/
367 +extern "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 +
384 +LExit:
385 + return hr;
386 +}
387 +
388 +
389 +/********************************************************************
390 +WcaFetchRecord() - gets the next record from a view on the installing database
391 +
392 +********************************************************************/
393 +extern "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 +
411 +LExit:
412 + return hr;
413 +}
414 +
415 +
416 +/********************************************************************
417 +WcaFetchSingleRecord() - gets a single record from a view on the installing database
418 +
419 +********************************************************************/
420 +extern "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 +
449 +LExit:
450 + return hr;
451 +}
452 +
453 +
454 +/********************************************************************
455 +WcaGetProperty - gets a string property value from the active install
456 +
457 +********************************************************************/
458 +extern "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_PTR cch = 0;
471 +
472 + if (!*ppwzData)
473 + {
474 + WCHAR szEmpty[1] = L"";
475 + er = ::MsiGetPropertyW(WcaGetInstallHandle(), wzProperty, szEmpty, (DWORD *)&cch);
476 + if (ERROR_MORE_DATA == er || ERROR_SUCCESS == er)
477 + {
478 + hr = StrAlloc(ppwzData, ++cch);
479 + }
480 + else
481 + {
482 + hr = HRESULT_FROM_WIN32(er);
483 + }
484 + ExitOnFailure(hr, "Failed to allocate string for Property '%ls'", wzProperty);
485 + }
486 + else
487 + {
488 + hr = StrMaxLength(*ppwzData, &cch);
489 + ExitOnFailure(hr, "Failed to get previous size of property data string.");
490 + }
491 +
492 + er = ::MsiGetPropertyW(WcaGetInstallHandle(), wzProperty, *ppwzData, (DWORD *)&cch);
493 + if (ERROR_MORE_DATA == er)
494 + {
495 + Assert(*ppwzData);
496 + hr = StrAlloc(ppwzData, ++cch);
497 + ExitOnFailure(hr, "Failed to allocate string for Property '%ls'", wzProperty);
498 +
499 + er = ::MsiGetPropertyW(WcaGetInstallHandle(), wzProperty, *ppwzData, (DWORD *)&cch);
500 + }
501 + ExitOnWin32Error(er, hr, "Failed to get data for property '%ls'", wzProperty);
502 +
503 +LExit:
504 + return hr;
505 +}
506 +
507 +
508 +/********************************************************************
509 +WcaGetFormattedProperty - gets a formatted string property value from
510 +the active install
511 +
512 +********************************************************************/
513 +extern "C" HRESULT WIXAPI WcaGetFormattedProperty(
514 + __in_z LPCWSTR wzProperty,
515 + __out LPWSTR* ppwzData
516 + )
517 +{
518 + if (!wzProperty || !*wzProperty || !ppwzData)
519 + {
520 + return E_INVALIDARG;
521 + }
522 +
523 + HRESULT hr = S_OK;
524 + LPWSTR pwzPropertyValue = NULL;
525 +
526 + hr = WcaGetProperty(wzProperty, &pwzPropertyValue);
527 + ExitOnFailure(hr, "failed to get %ls", wzProperty);
528 +
529 + hr = WcaGetFormattedString(pwzPropertyValue, ppwzData);
530 + ExitOnFailure(hr, "failed to get formatted value for property: '%ls' with value: '%ls'", wzProperty, pwzPropertyValue);
531 +
532 +LExit:
533 + ReleaseStr(pwzPropertyValue);
534 +
535 + return hr;
536 +}
537 +
538 +
539 +/********************************************************************
540 +WcaGetFormattedString - gets a formatted string value from
541 +the active install
542 +
543 +********************************************************************/
544 +extern "C" HRESULT WIXAPI WcaGetFormattedString(
545 + __in_z LPCWSTR wzString,
546 + __out LPWSTR* ppwzData
547 + )
548 +{
549 + if (!wzString || !*wzString || !ppwzData)
550 + {
551 + return E_INVALIDARG;
552 + }
553 +
554 + HRESULT hr = S_OK;
555 + UINT er = ERROR_SUCCESS;
556 + PMSIHANDLE hRecord = ::MsiCreateRecord(1);
557 + DWORD_PTR cch = 0;
558 +
559 + er = ::MsiRecordSetStringW(hRecord, 0, wzString);
560 + ExitOnWin32Error(er, hr, "Failed to set record field 0 with '%ls'", wzString);
561 +
562 + if (!*ppwzData)
563 + {
564 + WCHAR szEmpty[1] = L"";
565 + er = ::MsiFormatRecordW(WcaGetInstallHandle(), hRecord, szEmpty, (DWORD *)&cch);
566 + if (ERROR_MORE_DATA == er || ERROR_SUCCESS == er)
567 + {
568 + hr = StrAlloc(ppwzData, ++cch);
569 + }
570 + else
571 + {
572 + hr = HRESULT_FROM_WIN32(er);
573 + }
574 + ExitOnFailure(hr, "Failed to allocate string for formatted string: '%ls'", wzString);
575 + }
576 + else
577 + {
578 + hr = StrMaxLength(*ppwzData, &cch);
579 + ExitOnFailure(hr, "Failed to get previous size of property data string");
580 + }
581 +
582 + er = ::MsiFormatRecordW(WcaGetInstallHandle(), hRecord, *ppwzData, (DWORD *)&cch);
583 + if (ERROR_MORE_DATA == er)
584 + {
585 + hr = StrAlloc(ppwzData, ++cch);
586 + ExitOnFailure(hr, "Failed to allocate string for formatted string: '%ls'", wzString);
587 +
588 + er = ::MsiFormatRecordW(WcaGetInstallHandle(), hRecord, *ppwzData, (DWORD *)&cch);
589 + }
590 + ExitOnWin32Error(er, hr, "Failed to get formatted string: '%ls'", wzString);
591 +
592 +LExit:
593 + return hr;
594 +}
595 +
596 +
597 +/********************************************************************
598 +WcaGetIntProperty - gets an integer property value from the active install
599 +
600 +********************************************************************/
601 +extern "C" HRESULT WIXAPI WcaGetIntProperty(
602 + __in_z LPCWSTR wzProperty,
603 + __inout int* piData
604 + )
605 +{
606 + if (!piData)
607 + return E_INVALIDARG;
608 +
609 + HRESULT hr = S_OK;
610 + UINT er;
611 +
612 + WCHAR wzValue[32];
613 + DWORD cch = countof(wzValue) - 1;
614 +
615 + er = ::MsiGetPropertyW(WcaGetInstallHandle(), wzProperty, wzValue, &cch);
616 + ExitOnWin32Error(er, hr, "Failed to get data for property '%ls'", wzProperty);
617 +
618 + *piData = wcstol(wzValue, NULL, 10);
619 +
620 +LExit:
621 + return hr;
622 +}
623 +
624 +
625 +/********************************************************************
626 +WcaGetTargetPath - gets the target path for a specified folder
627 +
628 +********************************************************************/
629 +extern "C" HRESULT WIXAPI WcaGetTargetPath(
630 + __in_z LPCWSTR wzFolder,
631 + __out LPWSTR* ppwzData
632 + )
633 +{
634 + if (!wzFolder || !*wzFolder || !ppwzData)
635 + return E_INVALIDARG;
636 +
637 + HRESULT hr = S_OK;
638 +
639 + UINT er = ERROR_SUCCESS;
640 + DWORD_PTR cch = 0;
641 +
642 + if (!*ppwzData)
643 + {
644 + WCHAR szEmpty[1] = L"";
645 + er = ::MsiGetTargetPathW(WcaGetInstallHandle(), wzFolder, szEmpty, (DWORD*)&cch);
646 + if (ERROR_MORE_DATA == er || ERROR_SUCCESS == er)
647 + {
648 + ++cch; //Add one for the null terminator
649 + hr = StrAlloc(ppwzData, cch);
650 + }
651 + else
652 + {
653 + hr = HRESULT_FROM_WIN32(er);
654 + }
655 + ExitOnFailure(hr, "Failed to allocate string for target path of folder: '%ls'", wzFolder);
656 + }
657 + else
658 + {
659 + hr = StrMaxLength(*ppwzData, &cch);
660 + ExitOnFailure(hr, "Failed to get previous size of string");
661 + }
662 +
663 + er = ::MsiGetTargetPathW(WcaGetInstallHandle(), wzFolder, *ppwzData, (DWORD*)&cch);
664 + if (ERROR_MORE_DATA == er)
665 + {
666 + ++cch;
667 + hr = StrAlloc(ppwzData, cch);
668 + ExitOnFailure(hr, "Failed to allocate string for target path of folder: '%ls'", wzFolder);
669 +
670 + er = ::MsiGetTargetPathW(WcaGetInstallHandle(), wzFolder, *ppwzData, (DWORD*)&cch);
671 + }
672 + ExitOnWin32Error(er, hr, "Failed to get target path for folder '%ls'", wzFolder);
673 +
674 +LExit:
675 + return hr;
676 +}
677 +
678 +
679 +/********************************************************************
680 +WcaSetProperty - sets a string property value in the active install
681 +
682 +********************************************************************/
683 +extern "C" HRESULT WIXAPI WcaSetProperty(
684 + __in_z LPCWSTR wzPropertyName,
685 + __in_z LPCWSTR wzPropertyValue
686 + )
687 +{
688 + HRESULT hr = S_OK;
689 +
690 + if (!wzPropertyName || !*wzPropertyName || !wzPropertyValue)
691 + return E_INVALIDARG;
692 +
693 + UINT er = ::MsiSetPropertyW(WcaGetInstallHandle(), wzPropertyName, wzPropertyValue);
694 + ExitOnWin32Error(er, hr, "failed to set property: %ls", wzPropertyName);
695 +
696 +LExit:
697 + return hr;
698 +}
699 +
700 +
701 +/********************************************************************
702 +WcaSetIntProperty - sets a integer property value in the active install
703 +
704 +********************************************************************/
705 +extern "C" HRESULT WIXAPI WcaSetIntProperty(
706 + __in_z LPCWSTR wzPropertyName,
707 + __in int nPropertyValue
708 + )
709 +{
710 + if (!wzPropertyName || !*wzPropertyName)
711 + return E_INVALIDARG;
712 +
713 + // 12 characters should be enough for a 32-bit int: 10 digits, 1 sign, 1 null
714 + WCHAR wzPropertyValue[13];
715 + HRESULT hr = StringCchPrintfW(wzPropertyValue, countof(wzPropertyValue), L"%d", nPropertyValue);
716 + ExitOnFailure(hr, "failed to convert into string property value: %d", nPropertyValue);
717 +
718 + UINT er = ::MsiSetPropertyW(WcaGetInstallHandle(), wzPropertyName, wzPropertyValue);
719 + ExitOnWin32Error(er, hr, "failed to set property: %ls", wzPropertyName);
720 +
721 +LExit:
722 + return hr;
723 +}
724 +
725 +
726 +/********************************************************************
727 +WcaIsPropertySet() - returns TRUE if property is set
728 +
729 +********************************************************************/
730 +extern "C" BOOL WIXAPI WcaIsPropertySet(
731 + __in LPCSTR szProperty
732 + )
733 +{
734 + DWORD cchProperty = 0;
735 + char szEmpty[1] = "";
736 +#ifdef DEBUG
737 + UINT er =
738 +#endif
739 + ::MsiGetPropertyA(WcaGetInstallHandle(), szProperty, szEmpty, &cchProperty);
740 + AssertSz(ERROR_INVALID_PARAMETER != er && ERROR_INVALID_HANDLE != er, "Unexpected return value from ::MsiGetProperty()");
741 +
742 + return 0 < cchProperty; // property is set if the length is greater than zero
743 +}
744 +
745 +
746 +/********************************************************************
747 +WcaIsUnicodePropertySet() - returns TRUE if property is set
748 +
749 +********************************************************************/
750 +extern "C" BOOL WIXAPI WcaIsUnicodePropertySet(
751 + __in LPCWSTR wzProperty
752 + )
753 +{
754 + DWORD cchProperty = 0;
755 + wchar_t wzEmpty[1] = L"";
756 +#ifdef DEBUG
757 + UINT er =
758 +#endif
759 + ::MsiGetPropertyW(WcaGetInstallHandle(), wzProperty, wzEmpty, &cchProperty);
760 + AssertSz(ERROR_INVALID_PARAMETER != er && ERROR_INVALID_HANDLE != er, "Unexpected return value from ::MsiGetProperty()");
761 +
762 + return 0 < cchProperty; // property is set if the length is greater than zero
763 +}
764 +
765 +
766 +/********************************************************************
767 +WcaGetRecordInteger() - gets an integer field out of a record
768 +
769 +NOTE: returns S_FALSE if the field was null
770 +********************************************************************/
771 +extern "C" HRESULT WIXAPI WcaGetRecordInteger(
772 + __in MSIHANDLE hRec,
773 + __in UINT uiField,
774 + __inout int* piData
775 + )
776 +{
777 + if (!hRec || !piData)
778 + return E_INVALIDARG;
779 +
780 + HRESULT hr = S_OK;
781 + *piData = ::MsiRecordGetInteger(hRec, uiField);
782 + if (MSI_NULL_INTEGER == *piData)
783 + hr = S_FALSE;
784 +
785 + //LExit:
786 + return hr;
787 +}
788 +
789 +
790 +/********************************************************************
791 +WcaGetRecordString() - gets a string field out of a record
792 +
793 +********************************************************************/
794 +extern "C" HRESULT WIXAPI WcaGetRecordString(
795 + __in MSIHANDLE hRec,
796 + __in UINT uiField,
797 + __inout LPWSTR* ppwzData
798 + )
799 +{
800 + if (!hRec || !ppwzData)
801 + return E_INVALIDARG;
802 +
803 + HRESULT hr = S_OK;
804 + UINT er;
805 + DWORD_PTR cch = 0;
806 +
807 + if (!*ppwzData)
808 + {
809 + WCHAR szEmpty[1] = L"";
810 + er = ::MsiRecordGetStringW(hRec, uiField, szEmpty, (DWORD*)&cch);
811 + if (ERROR_MORE_DATA == er || ERROR_SUCCESS == er)
812 + {
813 + hr = StrAlloc(ppwzData, ++cch);
814 + }
815 + else
816 + {
817 + hr = HRESULT_FROM_WIN32(er);
818 + }
819 + ExitOnFailure(hr, "Failed to allocate memory for record string");
820 + }
821 + else
822 + {
823 + hr = StrMaxLength(*ppwzData, &cch);
824 + ExitOnFailure(hr, "Failed to get previous size of string");
825 + }
826 +
827 + er = ::MsiRecordGetStringW(hRec, uiField, *ppwzData, (DWORD*)&cch);
828 + if (ERROR_MORE_DATA == er)
829 + {
830 + hr = StrAlloc(ppwzData, ++cch);
831 + ExitOnFailure(hr, "Failed to allocate memory for record string");
832 +
833 + er = ::MsiRecordGetStringW(hRec, uiField, *ppwzData, (DWORD*)&cch);
834 + }
835 + ExitOnWin32Error(er, hr, "Failed to get string from record");
836 +
837 +LExit:
838 + return hr;
839 +}
840 +
841 +
842 +/********************************************************************
843 +HideNulls() - internal helper function to escape [~] in formatted strings
844 +
845 +********************************************************************/
846 +static void HideNulls(
847 + __inout_z LPWSTR wzData
848 + )
849 +{
850 + LPWSTR pwz = wzData;
851 +
852 + while(*pwz)
853 + {
854 + if (pwz[0] == L'[' && pwz[1] == L'~' && pwz[2] == L']') // found a null [~]
855 + {
856 + pwz[0] = L'!'; // turn it into !$!
857 + pwz[1] = L'$';
858 + pwz[2] = L'!';
859 + pwz += 3;
860 + }
861 + else
862 + {
863 + ++pwz;
864 + }
865 + }
866 +}
867 +
868 +
869 +/********************************************************************
870 +RevealNulls() - internal helper function to unescape !$! in formatted strings
871 +
872 +********************************************************************/
873 +static void RevealNulls(
874 + __inout_z LPWSTR wzData
875 + )
876 +{
877 + LPWSTR pwz = wzData;
878 +
879 + while(*pwz)
880 + {
881 + if (pwz[0] == L'!' && pwz[1] == L'$' && pwz[2] == L'!') // found the fake null !$!
882 + {
883 + pwz[0] = L'['; // turn it back into [~]
884 + pwz[1] = L'~';
885 + pwz[2] = L']';
886 + pwz += 3;
887 + }
888 + else
889 + {
890 + ++pwz;
891 + }
892 + }
893 +}
894 +
895 +
896 +/********************************************************************
897 +WcaGetRecordFormattedString() - gets formatted string filed from record
898 +
899 +********************************************************************/
900 +extern "C" HRESULT WIXAPI WcaGetRecordFormattedString(
901 + __in MSIHANDLE hRec,
902 + __in UINT uiField,
903 + __inout LPWSTR* ppwzData
904 + )
905 +{
906 + if (!hRec || !ppwzData)
907 + {
908 + return E_INVALIDARG;
909 + }
910 +
911 + HRESULT hr = S_OK;
912 + UINT er;
913 + DWORD_PTR cch = 0;
914 + PMSIHANDLE hRecFormat;
915 +
916 + // get the format string
917 + hr = WcaGetRecordString(hRec, uiField, ppwzData);
918 + ExitOnFailure(hr, "failed to get string from record");
919 +
920 + if (!**ppwzData)
921 + {
922 + ExitFunction();
923 + }
924 +
925 + // hide the nulls '[~]' so we can get them back after formatting
926 + HideNulls(*ppwzData);
927 +
928 + // set up the format record
929 + hRecFormat = ::MsiCreateRecord(1);
930 + ExitOnNull(hRecFormat, hr, E_UNEXPECTED, "Failed to create record to format string");
931 + hr = WcaSetRecordString(hRecFormat, 0, *ppwzData);
932 + ExitOnFailure(hr, "failed to set string to format record");
933 +
934 + // format the string
935 + hr = StrMaxLength(*ppwzData, &cch);
936 + ExitOnFailure(hr, "failed to get max length of string");
937 +
938 + er = ::MsiFormatRecordW(WcaGetInstallHandle(), hRecFormat, *ppwzData, (DWORD*)&cch);
939 + if (ERROR_MORE_DATA == er)
940 + {
941 + hr = StrAlloc(ppwzData, ++cch);
942 + ExitOnFailure(hr, "Failed to allocate memory for record string");
943 +
944 + er = ::MsiFormatRecordW(WcaGetInstallHandle(), hRecFormat, *ppwzData, (DWORD*)&cch);
945 + }
946 + ExitOnWin32Error(er, hr, "Failed to format string");
947 +
948 + // put the nulls back
949 + RevealNulls(*ppwzData);
950 +
951 +LExit:
952 + return hr;
953 +}
954 +
955 +
956 +/********************************************************************
957 +WcaGetRecordFormattedInteger() - gets formatted integer from record
958 +
959 +********************************************************************/
960 +extern "C" HRESULT WIXAPI WcaGetRecordFormattedInteger(
961 + __in MSIHANDLE hRec,
962 + __in UINT uiField,
963 + __out int* piData
964 + )
965 +{
966 + if (!hRec || !piData)
967 + {
968 + return E_INVALIDARG;
969 + }
970 +
971 + HRESULT hr = S_OK;
972 + LPWSTR pwzData = NULL;
973 +
974 + hr = WcaGetRecordFormattedString(hRec, uiField, &pwzData);
975 + ExitOnFailure(hr, "failed to get record field: %u", uiField);
976 + if (pwzData && *pwzData)
977 + {
978 + LPWSTR wz = NULL;
979 + *piData = wcstol(pwzData, &wz, 10);
980 + if (wz && *wz)
981 + {
982 + hr = E_INVALIDARG;
983 + ExitOnFailure(hr, "failed to parse record field: %u as number: %ls", uiField, pwzData);
984 + }
985 + }
986 + else
987 + {
988 + *piData = MSI_NULL_INTEGER;
989 + }
990 +
991 +LExit:
992 + return hr;
993 +}
994 +
995 +
996 +/********************************************************************
997 +WcaAllocStream() - creates a byte stream of the specified size
998 +
999 +NOTE: Use WcaFreeStream() to release the byte stream
1000 +********************************************************************/
1001 +extern "C" HRESULT WIXAPI WcaAllocStream(
1002 + __deref_out_bcount_part(cbData, 0) BYTE** ppbData,
1003 + __in DWORD cbData
1004 + )
1005 +{
1006 + Assert(ppbData);
1007 + HRESULT hr;
1008 + BYTE* pbNewData;
1009 +
1010 + if (*ppbData)
1011 + pbNewData = static_cast<BYTE*>(MemReAlloc(*ppbData, cbData, TRUE));
1012 + else
1013 + pbNewData = static_cast<BYTE*>(MemAlloc(cbData, TRUE));
1014 +
1015 + if (!pbNewData)
1016 + {
1017 + ExitOnLastError(hr, "Failed to allocate string");
1018 + }
1019 +
1020 + *ppbData = pbNewData;
1021 + pbNewData = NULL;
1022 +
1023 + hr = S_OK;
1024 +LExit:
1025 + ReleaseMem(pbNewData);
1026 +
1027 + return hr;
1028 +}
1029 +
1030 +
1031 +/********************************************************************
1032 +WcaFreeStream() - frees a byte stream
1033 +
1034 +********************************************************************/
1035 +extern "C" HRESULT WIXAPI WcaFreeStream(
1036 + __in BYTE* pbData
1037 + )
1038 +{
1039 + if (!pbData)
1040 + return E_INVALIDARG;
1041 +
1042 + HRESULT hr = MemFree(pbData);
1043 + return hr;
1044 +}
1045 +
1046 +
1047 +/********************************************************************
1048 +WcaGetRecordStream() - gets a byte stream field from record
1049 +
1050 +********************************************************************/
1051 +extern "C" HRESULT WIXAPI WcaGetRecordStream(
1052 + __in MSIHANDLE hRecBinary,
1053 + __in UINT uiField,
1054 + __deref_out_bcount_full(*pcbData) BYTE** ppbData,
1055 + __out DWORD* pcbData
1056 + )
1057 +{
1058 + HRESULT hr = S_OK;
1059 + UINT er = ERROR_SUCCESS;
1060 +
1061 + if (!hRecBinary || !ppbData || !pcbData)
1062 + return E_INVALIDARG;
1063 +
1064 + *pcbData = 0;
1065 + er = ::MsiRecordReadStream(hRecBinary, uiField, NULL, pcbData);
1066 + ExitOnWin32Error(er, hr, "failed to get size of stream");
1067 +
1068 + hr = WcaAllocStream(ppbData, *pcbData);
1069 + ExitOnFailure(hr, "failed to allocate data for stream");
1070 +
1071 + er = ::MsiRecordReadStream(hRecBinary, uiField, (char*)*ppbData, pcbData);
1072 + ExitOnWin32Error(er, hr, "failed to read from stream");
1073 +
1074 +LExit:
1075 + return hr;
1076 +}
1077 +
1078 +
1079 +/********************************************************************
1080 +WcaSetRecordString() - set a string field in record
1081 +
1082 +********************************************************************/
1083 +extern "C" HRESULT WIXAPI WcaSetRecordString(
1084 + __in MSIHANDLE hRec,
1085 + __in UINT uiField,
1086 + __in_z LPCWSTR wzData
1087 + )
1088 +{
1089 + if (!hRec || !wzData)
1090 + return E_INVALIDARG;
1091 +
1092 + HRESULT hr = S_OK;
1093 + UINT er = ::MsiRecordSetStringW(hRec, uiField, wzData);
1094 + ExitOnWin32Error(er, hr, "failed to set string in record");
1095 +
1096 +LExit:
1097 + return hr;
1098 +}
1099 +
1100 +
1101 +/********************************************************************
1102 +WcaSetRecordInteger() - set a integer field in record
1103 +
1104 +********************************************************************/
1105 +extern "C" HRESULT WIXAPI WcaSetRecordInteger(
1106 + __in MSIHANDLE hRec,
1107 + __in UINT uiField,
1108 + __in int iValue
1109 + )
1110 +{
1111 + if (!hRec)
1112 + return E_INVALIDARG;
1113 +
1114 + HRESULT hr = S_OK;
1115 + UINT er = ::MsiRecordSetInteger(hRec, uiField, iValue);
1116 + ExitOnWin32Error(er, hr, "failed to set integer in record");
1117 +
1118 +LExit:
1119 + return hr;
1120 +}
1121 +
1122 +
1123 +/********************************************************************
1124 +
1125 +WcaDoDeferredAction() - schedules an action at this point in the script
1126 +
1127 +********************************************************************/
1128 +extern "C" HRESULT WIXAPI WcaDoDeferredAction(
1129 + __in_z LPCWSTR wzAction,
1130 + __in_z LPCWSTR wzCustomActionData,
1131 + __in UINT uiCost
1132 + )
1133 +{
1134 + HRESULT hr = S_OK;
1135 + UINT er;
1136 +
1137 + if (wzCustomActionData && *wzCustomActionData)
1138 + {
1139 + er = ::MsiSetPropertyW(WcaGetInstallHandle(), wzAction, wzCustomActionData);
1140 + ExitOnWin32Error(er, hr, "Failed to set CustomActionData for deferred action");
1141 + }
1142 +
1143 + if (0 < uiCost)
1144 + {
1145 + hr = WcaProgressMessage(uiCost, TRUE); // add ticks to the progress bar
1146 + // TODO: handle the return codes correctly
1147 + }
1148 +
1149 + er = ::MsiDoActionW(WcaGetInstallHandle(), wzAction);
1150 + if (ERROR_INSTALL_USEREXIT == er)
1151 + {
1152 + WcaSetReturnValue(er);
1153 + }
1154 + ExitOnWin32Error(er, hr, "Failed MsiDoAction on deferred action");
1155 +
1156 +LExit:
1157 + return hr;
1158 +}
1159 +
1160 +
1161 +/********************************************************************
1162 +WcaCountOfCustomActionDataRecords() - counts the number of records
1163 +passed to a deferred CustomAction
1164 +
1165 +********************************************************************/
1166 +extern "C" DWORD WIXAPI WcaCountOfCustomActionDataRecords(
1167 + __in_z LPCWSTR wzData
1168 + )
1169 +{
1170 + WCHAR delim[] = {MAGIC_MULTISZ_DELIM, 0}; // magic char followed by NULL terminator
1171 + DWORD dwCount = 0;
1172 +
1173 + // 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
1174 + for (LPCWSTR pwzCurrent = wzData; pwzCurrent && *pwzCurrent && *(pwzCurrent + 1); pwzCurrent = wcsstr(pwzCurrent, delim))
1175 + {
1176 + ++dwCount;
1177 + ++pwzCurrent;
1178 + }
1179 +
1180 + return dwCount;
1181 +}
1182 +
1183 +
1184 +/********************************************************************
1185 +BreakDownCustomActionData() - internal helper to chop up CustomActionData
1186 +
1187 +NOTE: this modifies the passed in data
1188 +********************************************************************/
1189 +static LPWSTR BreakDownCustomActionData(
1190 + __inout LPWSTR* ppwzData
1191 + )
1192 +{
1193 + if (!ppwzData)
1194 + return NULL;
1195 + if (0 == *ppwzData)
1196 + return NULL;
1197 +
1198 + WCHAR delim[] = {MAGIC_MULTISZ_DELIM, 0}; // magic char followed by Null terminator
1199 +
1200 + LPWSTR pwzReturn = *ppwzData;
1201 + LPWSTR pwz = wcsstr(pwzReturn, delim);
1202 + if (pwz)
1203 + {
1204 + *pwz = 0;
1205 + *ppwzData = pwz + 1;
1206 + }
1207 + else
1208 + *ppwzData = 0;
1209 +
1210 + return pwzReturn;
1211 +}
1212 +
1213 +
1214 +/********************************************************************
1215 +RevertCustomActionData() - Reverts custom action data changes made
1216 + by BreakDownCustomActionData;
1217 +
1218 +NOTE: this modifies the passed in data
1219 +********************************************************************/
1220 +extern "C" void WIXAPI RevertCustomActionData(
1221 + __in LPWSTR wzRevertTo,
1222 + __in LPCWSTR wzRevertFrom
1223 + )
1224 +{
1225 + if (!wzRevertTo)
1226 + return;
1227 + if (!wzRevertFrom)
1228 + return;
1229 + // start at the revert point and replace all \0 with MAGIC_MULTISZ_DELIM
1230 + for(LPWSTR wzIndex = wzRevertTo; wzIndex < wzRevertFrom; wzIndex++)
1231 + {
1232 + if (0 == *wzIndex)
1233 + {
1234 + *wzIndex = MAGIC_MULTISZ_DELIM;
1235 + }
1236 + }
1237 + return;
1238 +}
1239 +
1240 +/********************************************************************
1241 +WcaReadStringFromCaData() - reads a string out of the CustomActionData
1242 +
1243 +NOTE: this modifies the passed in ppwzCustomActionData variable
1244 +********************************************************************/
1245 +extern "C" HRESULT WIXAPI WcaReadStringFromCaData(
1246 + __deref_in LPWSTR* ppwzCustomActionData,
1247 + __deref_out_z LPWSTR* ppwzString
1248 + )
1249 +{
1250 + HRESULT hr = S_OK;
1251 +
1252 + LPCWSTR pwz = BreakDownCustomActionData(ppwzCustomActionData);
1253 + if (!pwz)
1254 + return E_NOMOREITEMS;
1255 +
1256 + hr = StrAllocString(ppwzString, pwz, 0);
1257 + ExitOnFailure(hr, "failed to allocate memory for string");
1258 +
1259 + hr = S_OK;
1260 +LExit:
1261 + return hr;
1262 +}
1263 +
1264 +
1265 +/********************************************************************
1266 +WcaReadIntegerFromCaData() - reads an integer out of the CustomActionData
1267 +
1268 +NOTE: this modifies the passed in ppwzCustomActionData variable
1269 +********************************************************************/
1270 +extern "C" HRESULT WIXAPI WcaReadIntegerFromCaData(
1271 + __deref_in LPWSTR* ppwzCustomActionData,
1272 + __out int* piResult
1273 + )
1274 +{
1275 + LPCWSTR pwz = BreakDownCustomActionData(ppwzCustomActionData);
1276 + if (!pwz || 0 == wcslen(pwz))
1277 + return E_NOMOREITEMS;
1278 +
1279 + *piResult = wcstol(pwz, NULL, 10);
1280 + return S_OK;
1281 +}
1282 +
1283 +
1284 +/********************************************************************
1285 +WcaReadStreamFromCaData() - reads a stream out of the CustomActionData
1286 +
1287 +NOTE: this modifies the passed in ppwzCustomActionData variable
1288 +NOTE: returned stream should be freed with WcaFreeStream()
1289 +********************************************************************/
1290 +extern "C" HRESULT WIXAPI WcaReadStreamFromCaData(
1291 + __deref_in LPWSTR* ppwzCustomActionData,
1292 + __deref_out_bcount(*pcbData) BYTE** ppbData,
1293 + __out DWORD_PTR* pcbData
1294 + )
1295 +{
1296 + HRESULT hr;
1297 +
1298 + LPCWSTR pwz = BreakDownCustomActionData(ppwzCustomActionData);
1299 + if (!pwz)
1300 + return E_NOMOREITEMS;
1301 +
1302 + hr = StrAllocBase85Decode(pwz, ppbData, pcbData);
1303 + ExitOnFailure(hr, "failed to decode string into stream");
1304 +
1305 +LExit:
1306 + return hr;
1307 +}
1308 +
1309 +
1310 +/********************************************************************
1311 +WcaWriteStringToCaData() - adds a string to the CustomActionData to
1312 +feed a deferred CustomAction
1313 +
1314 +********************************************************************/
1315 +extern "C" HRESULT WIXAPI WcaWriteStringToCaData(
1316 + __in_z LPCWSTR wzString,
1317 + __deref_inout_z_opt LPWSTR* ppwzCustomActionData
1318 + )
1319 +{
1320 + HRESULT hr = S_OK;
1321 + WCHAR delim[] = {MAGIC_MULTISZ_DELIM, 0}; // magic char followed by NULL terminator
1322 +
1323 + if (!ppwzCustomActionData)
1324 + {
1325 + ExitFunction1(hr = E_INVALIDARG);
1326 + }
1327 +
1328 + DWORD cchString = lstrlenW(wzString) + 1; // assume we'll be adding the delim
1329 + DWORD_PTR cchCustomActionData = 0;
1330 +
1331 + if (*ppwzCustomActionData)
1332 + {
1333 + hr = StrMaxLength(*ppwzCustomActionData, &cchCustomActionData);
1334 + ExitOnFailure(hr, "failed to get length of custom action data");
1335 + }
1336 +
1337 + if ((cchCustomActionData - lstrlenW(*ppwzCustomActionData)) < cchString + 1)
1338 + {
1339 + cchCustomActionData += cchString + 1 + 255; // add 255 for good measure
1340 + hr = StrAlloc(ppwzCustomActionData, cchCustomActionData);
1341 + ExitOnFailure(hr, "Failed to allocate memory for CustomActionData string");
1342 + }
1343 +
1344 + if (**ppwzCustomActionData) // if data exists toss the delimiter on before adding more to the end
1345 + {
1346 + hr = ::StringCchCatW(*ppwzCustomActionData, cchCustomActionData, delim);
1347 + ExitOnFailure(hr, "Failed to concatenate CustomActionData string");
1348 + }
1349 +
1350 + hr = ::StringCchCatW(*ppwzCustomActionData, cchCustomActionData, wzString);
1351 + ExitOnFailure(hr, "Failed to concatenate CustomActionData string");
1352 +
1353 +LExit:
1354 + return hr;
1355 +}
1356 +
1357 +
1358 +/********************************************************************
1359 +WcaWriteIntegerToCaData() - adds an integer to the CustomActionData to
1360 +feed a deferred CustomAction
1361 +
1362 +********************************************************************/
1363 +extern "C" HRESULT WIXAPI WcaWriteIntegerToCaData(
1364 + __in int i,
1365 + __deref_out_z_opt LPWSTR* ppwzCustomActionData
1366 + )
1367 +{
1368 + WCHAR wzBuffer[13];
1369 + StringCchPrintfW(wzBuffer, countof(wzBuffer), L"%d", i);
1370 +
1371 + return WcaWriteStringToCaData(wzBuffer, ppwzCustomActionData);
1372 +}
1373 +
1374 +
1375 +/********************************************************************
1376 +WcaWriteStreamToCaData() - adds a byte stream to the CustomActionData to
1377 +feed a deferred CustomAction
1378 +
1379 +********************************************************************/
1380 +extern "C" HRESULT WIXAPI WcaWriteStreamToCaData(
1381 + __in_bcount(cbData) const BYTE* pbData,
1382 + __in DWORD cbData,
1383 + __deref_inout_z_opt LPWSTR* ppwzCustomActionData
1384 + )
1385 +{
1386 + HRESULT hr;
1387 + LPWSTR pwzData = NULL;
1388 +
1389 + hr = StrAllocBase85Encode(pbData, cbData, &pwzData);
1390 + ExitOnFailure(hr, "failed to encode data into string");
1391 +
1392 + hr = WcaWriteStringToCaData(pwzData, ppwzCustomActionData);
1393 +
1394 +LExit:
1395 + ReleaseStr(pwzData);
1396 + return hr;
1397 +}
1398 +
1399 +
1400 +/********************************************************************
1401 +WcaAddTempRecord - adds a temporary record to the active database
1402 +
1403 +NOTE: you cannot use PMSIHANDLEs for the __in/__out parameters
1404 +NOTE: uiUniquifyColumn can be 0 if no column needs to be made unique
1405 +********************************************************************/
1406 +extern "C" HRESULT __cdecl WcaAddTempRecord(
1407 + __inout MSIHANDLE* phTableView,
1408 + __inout MSIHANDLE* phColumns,
1409 + __in_z LPCWSTR wzTable,
1410 + __out_opt MSIDBERROR* pdbError,
1411 + __in UINT uiUniquifyColumn,
1412 + __in UINT cColumns,
1413 + ...
1414 + )
1415 +{
1416 + Assert(phTableView && phColumns);
1417 +
1418 + static DWORD dwUniquifyValue = ::GetTickCount();
1419 +
1420 + HRESULT hr = S_OK;
1421 + UINT er = ERROR_SUCCESS;
1422 +
1423 + LPWSTR pwzQuery = NULL;
1424 + PMSIHANDLE hTempRec;
1425 + DWORD i;
1426 + va_list args;
1427 +
1428 + LPWSTR pwzData = NULL;
1429 + LPWSTR pwzUniquify = NULL;
1430 +
1431 + //
1432 + // if we don't have a table and its columns already
1433 + //
1434 + if (NULL == *phTableView)
1435 + {
1436 + // set the query
1437 + hr = StrAllocFormatted(&pwzQuery, L"SELECT * FROM `%s`",wzTable);
1438 + ExitOnFailure(hr, "failed to allocate string for query");
1439 +
1440 + // Open and Execute the temp View
1441 + hr = WcaOpenExecuteView(pwzQuery, phTableView);
1442 + ExitOnFailure(hr, "failed to openexecute temp view with query %ls", pwzQuery);
1443 + }
1444 +
1445 + if (NULL == *phColumns)
1446 + {
1447 + // use GetColumnInfo to populate the datatype record
1448 + er = ::MsiViewGetColumnInfo(*phTableView, MSICOLINFO_TYPES, phColumns);
1449 + ExitOnWin32Error(er, hr, "failed to columns for table: %ls", wzTable);
1450 + }
1451 + AssertSz(::MsiRecordGetFieldCount(*phColumns) == cColumns, "passed in argument does not match number of columns in table");
1452 +
1453 + //
1454 + // create the temp record
1455 + //
1456 + hTempRec = ::MsiCreateRecord(cColumns);
1457 + ExitOnNull(hTempRec, hr, E_UNEXPECTED, "could not create temp record for table: %ls", wzTable);
1458 +
1459 + //
1460 + // loop through all the columns filling in the data
1461 + //
1462 + va_start(args, cColumns);
1463 + for (i = 1; i <= cColumns; i++)
1464 + {
1465 + hr = WcaGetRecordString(*phColumns, i, &pwzData);
1466 + ExitOnFailure(hr, "failed to get the data type for %d", i);
1467 +
1468 + // if data type is string write string
1469 + if (L's' == *pwzData || L'S' == *pwzData || L'g' == *pwzData || L'G' == *pwzData || L'l' == *pwzData || L'L' == *pwzData)
1470 + {
1471 + LPCWSTR wz = va_arg(args, WCHAR*);
1472 +
1473 + // if this is the column that is supposed to be unique add the time stamp on the end
1474 + if (uiUniquifyColumn == i)
1475 + {
1476 + hr = StrAllocFormatted(&pwzUniquify, L"%s%u", wz, ++dwUniquifyValue); // up the count so we have no collisions on the unique name
1477 + ExitOnFailure(hr, "failed to allocate string for unique column: %d", uiUniquifyColumn);
1478 +
1479 + wz = pwzUniquify;
1480 + }
1481 +
1482 + er = ::MsiRecordSetStringW(hTempRec, i, wz);
1483 + ExitOnWin32Error(er, hr, "failed to set string value at position %d", i);
1484 + }
1485 + // if data type is integer write integer
1486 + else if (L'i' == *pwzData || L'I' == *pwzData || L'j' == *pwzData || L'J' == *pwzData)
1487 + {
1488 + AssertSz(uiUniquifyColumn != i, "Cannot uniquify an integer column");
1489 + int iData = va_arg(args, int);
1490 +
1491 + er = ::MsiRecordSetInteger(hTempRec, i, iData);
1492 + ExitOnWin32Error(er, hr, "failed to set integer value at position %d", i);
1493 + }
1494 + else
1495 + {
1496 + // not supporting binary streams so error out
1497 + hr = HRESULT_FROM_WIN32(ERROR_DATATYPE_MISMATCH);
1498 + ExitOnRootFailure(hr, "unsupported data type '%ls' in column: %d", pwzData, i);
1499 + }
1500 + }
1501 + va_end(args);
1502 +
1503 + //
1504 + // add the temporary record to the MSI
1505 + //
1506 + er = ::MsiViewModify(*phTableView, MSIMODIFY_INSERT_TEMPORARY, hTempRec);
1507 + hr = HRESULT_FROM_WIN32(er);
1508 + if (FAILED(hr))
1509 + {
1510 + if (pdbError)
1511 + {
1512 + // MSI provides only a generic ERROR_FUNCTION_FAILED if a temporary row
1513 + // can't be inserted; if we're being asked to provide the detailed error,
1514 + // get it using the MSIMODIFY_VALIDATE_NEW flag
1515 + er = ::MsiViewModify(*phTableView, MSIMODIFY_VALIDATE_NEW, hTempRec);
1516 + hr = HRESULT_FROM_WIN32(er);
1517 + }
1518 +
1519 + WCHAR wzBuf[MAX_PATH];
1520 + DWORD cchBuf = countof(wzBuf);
1521 + MSIDBERROR dbErr = ::MsiViewGetErrorW(*phTableView, wzBuf, &cchBuf);
1522 + if (pdbError)
1523 + {
1524 + *pdbError = dbErr;
1525 + }
1526 + ExitOnFailure(hr, "failed to add temporary row, dberr: %d, err: %ls", dbErr, wzBuf);
1527 + }
1528 +
1529 +LExit:
1530 + ReleaseStr(pwzUniquify);
1531 + ReleaseStr(pwzData);
1532 + ReleaseStr(pwzQuery);
1533 +
1534 + return hr;
1535 +}
1536 +
1537 +
1538 +/********************************************************************
1539 +WcaDumpTable - dumps a table to the log file
1540 +
1541 +********************************************************************/
1542 +extern "C" HRESULT WIXAPI WcaDumpTable(
1543 + __in_z LPCWSTR wzTable
1544 + )
1545 +{
1546 + HRESULT hr = S_OK;
1547 + UINT er = ERROR_SUCCESS;
1548 +
1549 + LPWSTR pwzQuery = NULL;
1550 + PMSIHANDLE hView;
1551 + PMSIHANDLE hColumns;
1552 + DWORD cColumns = 0;
1553 + PMSIHANDLE hRec;
1554 +
1555 + LPWSTR pwzData = NULL;
1556 + LPWSTR pwzPrint = NULL;
1557 +
1558 + hr = StrAllocFormatted(&pwzQuery, L"SELECT * FROM `%s`",wzTable);
1559 + ExitOnFailure(hr, "failed to allocate string for query");
1560 +
1561 + // Open and Execute the temp View
1562 + hr = WcaOpenExecuteView(pwzQuery, &hView);
1563 + ExitOnFailure(hr, "failed to openexecute temp view with query %ls", pwzQuery);
1564 +
1565 + // Use GetColumnInfo to populate the names of the columns.
1566 + er = ::MsiViewGetColumnInfo(hView, MSICOLINFO_NAMES, &hColumns);
1567 + hr = HRESULT_FROM_WIN32(er);
1568 + ExitOnFailure(hr, "failed to get column info for table: %ls", wzTable);
1569 +
1570 + cColumns = ::MsiRecordGetFieldCount(hColumns);
1571 +
1572 + WcaLog(LOGMSG_STANDARD, "--- Begin Table Dump %ls ---", wzTable);
1573 +
1574 + // Loop through all the columns filling in the data.
1575 + for (DWORD i = 1; i <= cColumns; i++)
1576 + {
1577 + hr = WcaGetRecordString(hColumns, i, &pwzData);
1578 + ExitOnFailure(hr, "failed to get the column name for %d", i);
1579 +
1580 + hr = StrAllocConcat(&pwzPrint, pwzData, 0);
1581 + ExitOnFailure(hr, "Failed to add column name.");
1582 +
1583 + hr = StrAllocConcat(&pwzPrint, L"\t", 1);
1584 + ExitOnFailure(hr, "Failed to add column name.");
1585 + }
1586 +
1587 + WcaLog(LOGMSG_STANDARD, "%ls", pwzPrint);
1588 +
1589 + // Now dump the actual rows.
1590 + while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
1591 + {
1592 + if (pwzPrint && *pwzPrint)
1593 + {
1594 + *pwzPrint = L'\0';
1595 + }
1596 +
1597 + for (DWORD i = 1; i <= cColumns; i++)
1598 + {
1599 + hr = WcaGetRecordString(hRec, i, &pwzData);
1600 + ExitOnFailure(hr, "failed to get the column name for %d", i);
1601 +
1602 + hr = StrAllocConcat(&pwzPrint, pwzData, 0);
1603 + ExitOnFailure(hr, "Failed to add column name.");
1604 +
1605 + hr = StrAllocConcat(&pwzPrint, L"\t", 1);
1606 + ExitOnFailure(hr, "Failed to add column name.");
1607 + }
1608 +
1609 + WcaLog(LOGMSG_STANDARD, "%ls", pwzPrint);
1610 + }
1611 +
1612 + WcaLog(LOGMSG_STANDARD, "--- End Table Dump %ls ---", wzTable);
1613 +
1614 +LExit:
1615 + ReleaseStr(pwzPrint);
1616 + ReleaseStr(pwzData);
1617 + ReleaseStr(pwzQuery);
1618 +
1619 + return hr;
1620 +}
1621 +
1622 +
1623 +HRESULT WIXAPI WcaDeferredActionRequiresReboot()
1624 +{
1625 + HRESULT hr = S_OK;
1626 + ATOM atomReboot = 0;
1627 +
1628 + atomReboot = ::GlobalAddAtomW(L"WcaDeferredActionRequiresReboot");
1629 + ExitOnNullWithLastError(atomReboot, hr, "Failed to create WcaDeferredActionRequiresReboot global atom.");
1630 +
1631 +LExit:
1632 + return hr;
1633 +}
1634 +
1635 +
1636 +BOOL WIXAPI WcaDidDeferredActionRequireReboot()
1637 +{
1638 + // NOTE: This function does not delete the global atom. That is done
1639 + // purposefully so that any other installs that occur after this point also
1640 + // require a reboot.
1641 + ATOM atomReboot = ::GlobalFindAtomW(L"WcaDeferredActionRequiresReboot");
1642 + return 0 != atomReboot;
1643 +}
src/wcautil/wcawrapquery.cpp new
+717
@@ -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 +
6 +static const LPWSTR ISINSTALLEDCOLUMNNAME = L"ISInstalled";
7 +static const LPWSTR ISACTIONCOLUMNNAME = L"ISAction";
8 +static const LPWSTR SOURCEPATHCOLUMNNAME = L"SourcePath";
9 +static const LPWSTR TARGETPATHCOLUMNNAME = L"TargetPath";
10 +
11 +// This instantiates a new query object in the deferred CA, and returns the handle to the query
12 +WCA_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 +
74 +LExit:
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.
87 +eColumnDataType WIXAPI GetDataTypeFromString(
88 + LPCWSTR pwzTypeString
89 + )
90 +{
91 + if (NULL == pwzTypeString || 0 == wcslen(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 +
123 +HRESULT 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 +
145 +LExit:
146 + return hr;
147 +}
148 +
149 +/********************************************************************
150 +WcaWrapQuery() - wraps a view and transmits it through the
151 + CustomActionData property
152 +
153 +********************************************************************/
154 +HRESULT 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 (0 == lstrlenW(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 +
465 +LExit:
466 + ReleaseStr(pwzData);
467 + ReleaseStr(pwzColumnData);
468 + ReleaseStr(pwzRecordData);
469 +
470 + ReleaseMem(pbData);
471 +
472 + return hr;
473 +}
474 +
475 +
476 +/********************************************************************
477 +WcaBeginUnwrapQuery() - unwraps a view for direct access from the
478 + CustomActionData property
479 +
480 +********************************************************************/
481 +HRESULT 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 +
594 +LExit:
595 + ReleaseStr(pwzData);
596 + ReleaseMem(pbData);
597 +
598 + return hr;
599 +}
600 +
601 +// This function returns the total number of records in a query
602 +DWORD 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
610 +void 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!
620 +HRESULT 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!
648 +HRESULT 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 +
678 +LExit:
679 + ReleaseStr(pwzData);
680 +
681 + return hr;
682 +}
683 +
684 +/********************************************************************
685 +WcaBeginUnwrapQuery() - Finishes unwrapping a view for direct access
686 + from the CustomActionData property
687 +
688 +********************************************************************/
689 +void 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 +}
version.json new
+11
@@ -0,0 +1,11 @@
1 +{
2 + "version": "4.0",
3 + "publicReleaseRefSpec": [
4 + "^refs/heads/master$"
5 + ],
6 + "cloudBuild": {
7 + "buildNumber": {
8 + "enabled": true
9 + }
10 + }
11 +}
wcautil.sln new
+31
@@ -0,0 +1,31 @@
1 +
2 +Microsoft Visual Studio Solution File, Format Version 12.00
3 +# Visual Studio 15
4 +VisualStudioVersion = 15.0.26730.12
5 +MinimumVisualStudioVersion = 15.0.26124.0
6 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wcautil", "src\wcautil\wcautil.vcxproj", "{1244E671-F108-4334-BA52-8A7517F26ECD}"
7 +EndProject
8 +Global
9 + GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 + Debug|x64 = Debug|x64
11 + Debug|x86 = Debug|x86
12 + Release|x64 = Release|x64
13 + Release|x86 = Release|x86
14 + EndGlobalSection
15 + GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 + {1244E671-F108-4334-BA52-8A7517F26ECD}.Debug|x64.ActiveCfg = Debug|x64
17 + {1244E671-F108-4334-BA52-8A7517F26ECD}.Debug|x64.Build.0 = Debug|x64
18 + {1244E671-F108-4334-BA52-8A7517F26ECD}.Debug|x86.ActiveCfg = Debug|Win32
19 + {1244E671-F108-4334-BA52-8A7517F26ECD}.Debug|x86.Build.0 = Debug|Win32
20 + {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|x64.ActiveCfg = Release|x64
21 + {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|x64.Build.0 = Release|x64
22 + {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|x86.ActiveCfg = Release|Win32
23 + {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|x86.Build.0 = Release|Win32
24 + EndGlobalSection
25 + GlobalSection(SolutionProperties) = preSolution
26 + HideSolutionNode = FALSE
27 + EndGlobalSection
28 + GlobalSection(ExtensibilityGlobals) = postSolution
29 + SolutionGuid = {DD209744-C40E-4C34-8CB4-BC6B71F9A133}
30 + EndGlobalSection
31 +EndGlobal