aboutsummaryrefslogtreecommitdiff
path: root/src/libs
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2021-04-22 06:38:23 -0700
committerRob Mensching <rob@firegiant.com>2021-04-29 16:21:09 -0700
commit7f642e51670bc38a4ef782a363936850bc2b0ba9 (patch)
tree19684b2d94979f130c0935328f0d44cf006e45ef /src/libs
parentf39e7a3e164d0736e45049e5726d0da2013da3c9 (diff)
downloadwix-7f642e51670bc38a4ef782a363936850bc2b0ba9.tar.gz
wix-7f642e51670bc38a4ef782a363936850bc2b0ba9.tar.bz2
wix-7f642e51670bc38a4ef782a363936850bc2b0ba9.zip
Move dutil into libs/dutil
Diffstat (limited to 'src/libs')
-rw-r--r--src/libs/dutil/CustomizedNativeRecommendedRules.ruleset8
-rw-r--r--src/libs/dutil/Directory.Build.props26
-rw-r--r--src/libs/dutil/Directory.Build.targets73
-rw-r--r--src/libs/dutil/Directory.csproj.props13
-rw-r--r--src/libs/dutil/Directory.vcxproj.props115
-rw-r--r--src/libs/dutil/NativeMultiTargeting.Build.props10
-rw-r--r--src/libs/dutil/README.md2
-rw-r--r--src/libs/dutil/WixToolset.DUtil/acl2util.cpp135
-rw-r--r--src/libs/dutil/WixToolset.DUtil/aclutil.cpp1044
-rw-r--r--src/libs/dutil/WixToolset.DUtil/apputil.cpp124
-rw-r--r--src/libs/dutil/WixToolset.DUtil/apuputil.cpp700
-rw-r--r--src/libs/dutil/WixToolset.DUtil/atomutil.cpp1297
-rw-r--r--src/libs/dutil/WixToolset.DUtil/buffutil.cpp529
-rw-r--r--src/libs/dutil/WixToolset.DUtil/build/WixToolset.DUtil.props28
-rw-r--r--src/libs/dutil/WixToolset.DUtil/butil.cpp257
-rw-r--r--src/libs/dutil/WixToolset.DUtil/cabcutil.cpp1539
-rw-r--r--src/libs/dutil/WixToolset.DUtil/cabutil.cpp617
-rw-r--r--src/libs/dutil/WixToolset.DUtil/certutil.cpp342
-rw-r--r--src/libs/dutil/WixToolset.DUtil/conutil.cpp673
-rw-r--r--src/libs/dutil/WixToolset.DUtil/cryputil.cpp404
-rw-r--r--src/libs/dutil/WixToolset.DUtil/deputil.cpp699
-rw-r--r--src/libs/dutil/WixToolset.DUtil/dictutil.cpp784
-rw-r--r--src/libs/dutil/WixToolset.DUtil/dirutil.cpp441
-rw-r--r--src/libs/dutil/WixToolset.DUtil/dlutil.cpp802
-rw-r--r--src/libs/dutil/WixToolset.DUtil/dpiutil.cpp274
-rw-r--r--src/libs/dutil/WixToolset.DUtil/dutil.cpp524
-rw-r--r--src/libs/dutil/WixToolset.DUtil/dutil.nuspec27
-rw-r--r--src/libs/dutil/WixToolset.DUtil/dutil.vcxproj183
-rw-r--r--src/libs/dutil/WixToolset.DUtil/dutil.vcxproj.filters372
-rw-r--r--src/libs/dutil/WixToolset.DUtil/eseutil.cpp1340
-rw-r--r--src/libs/dutil/WixToolset.DUtil/fileutil.cpp2032
-rw-r--r--src/libs/dutil/WixToolset.DUtil/gdiputil.cpp227
-rw-r--r--src/libs/dutil/WixToolset.DUtil/guidutil.cpp54
-rw-r--r--src/libs/dutil/WixToolset.DUtil/iis7util.cpp535
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/aclutil.h154
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/apputil.h45
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/apuputil.h87
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/atomutil.h146
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/buffutil.h91
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/butil.h60
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/cabcutil.h62
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/cabutil.h56
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/certutil.h66
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/conutil.h80
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/cryputil.h106
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/deputil.h147
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/dictutil.h69
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/dirutil.h59
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/dlutil.h59
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/dpiutil.h120
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/dutil.h190
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/dutilsources.h76
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/eseutil.h223
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/fileutil.h247
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/gdiputil.h39
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/guidutil.h21
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/iis7util.h222
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/inetutil.h39
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/iniutil.h79
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/jsonutil.h112
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/locutil.h120
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/logutil.h192
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/memutil.h80
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/metautil.h52
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/monutil.h108
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/osutil.h42
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/pathutil.h252
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/perfutil.h24
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/polcutil.h39
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/procutil.h75
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/regutil.h246
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/resrutil.h43
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/reswutil.h31
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/rexutil.h54
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/rmutil.h46
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/rssutil.h89
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/sceutil.h273
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/sczutil.h30
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/shelutil.h47
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/sqlutil.h136
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/srputil.h45
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/strutil.h316
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/svcutil.h21
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/thmutil.h765
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/timeutil.h38
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/uncutil.h20
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/uriutil.h100
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/userutil.h32
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/verutil.h93
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/wiutil.h402
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/wuautil.h19
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/xmlutil.h167
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inetutil.cpp155
-rw-r--r--src/libs/dutil/WixToolset.DUtil/iniutil.cpp768
-rw-r--r--src/libs/dutil/WixToolset.DUtil/jsonutil.cpp687
-rw-r--r--src/libs/dutil/WixToolset.DUtil/locutil.cpp628
-rw-r--r--src/libs/dutil/WixToolset.DUtil/logutil.cpp961
-rw-r--r--src/libs/dutil/WixToolset.DUtil/memutil.cpp336
-rw-r--r--src/libs/dutil/WixToolset.DUtil/metautil.cpp378
-rw-r--r--src/libs/dutil/WixToolset.DUtil/monutil.cpp2019
-rw-r--r--src/libs/dutil/WixToolset.DUtil/osutil.cpp251
-rw-r--r--src/libs/dutil/WixToolset.DUtil/packages.config7
-rw-r--r--src/libs/dutil/WixToolset.DUtil/path2utl.cpp104
-rw-r--r--src/libs/dutil/WixToolset.DUtil/pathutil.cpp1083
-rw-r--r--src/libs/dutil/WixToolset.DUtil/perfutil.cpp82
-rw-r--r--src/libs/dutil/WixToolset.DUtil/polcutil.cpp126
-rw-r--r--src/libs/dutil/WixToolset.DUtil/precomp.h98
-rw-r--r--src/libs/dutil/WixToolset.DUtil/proc2utl.cpp83
-rw-r--r--src/libs/dutil/WixToolset.DUtil/proc3utl.cpp129
-rw-r--r--src/libs/dutil/WixToolset.DUtil/procutil.cpp522
-rw-r--r--src/libs/dutil/WixToolset.DUtil/regutil.cpp1035
-rw-r--r--src/libs/dutil/WixToolset.DUtil/resrutil.cpp266
-rw-r--r--src/libs/dutil/WixToolset.DUtil/reswutil.cpp386
-rw-r--r--src/libs/dutil/WixToolset.DUtil/rexutil.cpp601
-rw-r--r--src/libs/dutil/WixToolset.DUtil/rmutil.cpp488
-rw-r--r--src/libs/dutil/WixToolset.DUtil/rssutil.cpp647
-rw-r--r--src/libs/dutil/WixToolset.DUtil/sceutil.cpp2489
-rw-r--r--src/libs/dutil/WixToolset.DUtil/shelutil.cpp342
-rw-r--r--src/libs/dutil/WixToolset.DUtil/sqlutil.cpp1002
-rw-r--r--src/libs/dutil/WixToolset.DUtil/srputil.cpp252
-rw-r--r--src/libs/dutil/WixToolset.DUtil/strutil.cpp2824
-rw-r--r--src/libs/dutil/WixToolset.DUtil/svcutil.cpp59
-rw-r--r--src/libs/dutil/WixToolset.DUtil/thmutil.cpp5709
-rw-r--r--src/libs/dutil/WixToolset.DUtil/timeutil.cpp385
-rw-r--r--src/libs/dutil/WixToolset.DUtil/uncutil.cpp69
-rw-r--r--src/libs/dutil/WixToolset.DUtil/uriutil.cpp553
-rw-r--r--src/libs/dutil/WixToolset.DUtil/userutil.cpp300
-rw-r--r--src/libs/dutil/WixToolset.DUtil/verutil.cpp647
-rw-r--r--src/libs/dutil/WixToolset.DUtil/wiutil.cpp1629
-rw-r--r--src/libs/dutil/WixToolset.DUtil/wuautil.cpp104
-rw-r--r--src/libs/dutil/WixToolset.DUtil/xmlutil.cpp1332
-rw-r--r--src/libs/dutil/WixToolset.DUtil/xsd/thmutil.xsd1188
-rw-r--r--src/libs/dutil/appveyor.cmd24
-rw-r--r--src/libs/dutil/appveyor.yml44
-rw-r--r--src/libs/dutil/dutil.sln47
-rw-r--r--src/libs/dutil/nuget.config8
-rw-r--r--src/libs/dutil/test/DUtilUnitTest/ApupUtilTests.cpp46
-rw-r--r--src/libs/dutil/test/DUtilUnitTest/AssemblyInfo.cpp12
-rw-r--r--src/libs/dutil/test/DUtilUnitTest/DUtilTests.cpp35
-rw-r--r--src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj99
-rw-r--r--src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters80
-rw-r--r--src/libs/dutil/test/DUtilUnitTest/DictUtilTest.cpp191
-rw-r--r--src/libs/dutil/test/DUtilUnitTest/DirUtilTests.cpp70
-rw-r--r--src/libs/dutil/test/DUtilUnitTest/FileUtilTest.cpp125
-rw-r--r--src/libs/dutil/test/DUtilUnitTest/GuidUtilTest.cpp60
-rw-r--r--src/libs/dutil/test/DUtilUnitTest/IniUtilTest.cpp345
-rw-r--r--src/libs/dutil/test/DUtilUnitTest/MemUtilTest.cpp505
-rw-r--r--src/libs/dutil/test/DUtilUnitTest/MonUtilTest.cpp487
-rw-r--r--src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp80
-rw-r--r--src/libs/dutil/test/DUtilUnitTest/SceUtilTest.cpp488
-rw-r--r--src/libs/dutil/test/DUtilUnitTest/StrUtilTest.cpp192
-rw-r--r--src/libs/dutil/test/DUtilUnitTest/TestData/ApupUtilTests/FeedBv2.0.xml68
-rw-r--r--src/libs/dutil/test/DUtilUnitTest/UnitTest.rc6
-rw-r--r--src/libs/dutil/test/DUtilUnitTest/UriUtilTest.cpp98
-rw-r--r--src/libs/dutil/test/DUtilUnitTest/VerUtilTests.cpp933
-rw-r--r--src/libs/dutil/test/DUtilUnitTest/error.cpp26
-rw-r--r--src/libs/dutil/test/DUtilUnitTest/error.h14
-rw-r--r--src/libs/dutil/test/DUtilUnitTest/packages.config13
-rw-r--r--src/libs/dutil/test/DUtilUnitTest/precomp.cpp3
-rw-r--r--src/libs/dutil/test/DUtilUnitTest/precomp.h32
-rw-r--r--src/libs/dutil/test/DUtilUnitTest/resource.h2
161 files changed, 57739 insertions, 0 deletions
diff --git a/src/libs/dutil/CustomizedNativeRecommendedRules.ruleset b/src/libs/dutil/CustomizedNativeRecommendedRules.ruleset
new file mode 100644
index 00000000..142b141c
--- /dev/null
+++ b/src/libs/dutil/CustomizedNativeRecommendedRules.ruleset
@@ -0,0 +1,8 @@
1<?xml version="1.0" encoding="utf-8"?>
2<RuleSet Name="Customized Microsoft Native Recommended Rules" Description="Microsoft Native Recommended Rules, -C26812" ToolsVersion="16.0">
3 <Include Path="nativerecommendedrules.ruleset" Action="Default" />
4 <Rules AnalyzerId="Microsoft.Analyzers.NativeCodeAnalysis" RuleNamespace="Microsoft.Rules.Native">
5 <!-- We need C style enums since we support BAs written in C -->
6 <Rule Id="C26812" Action="None" />
7 </Rules>
8</RuleSet> \ No newline at end of file
diff --git a/src/libs/dutil/Directory.Build.props b/src/libs/dutil/Directory.Build.props
new file mode 100644
index 00000000..fb34d54e
--- /dev/null
+++ b/src/libs/dutil/Directory.Build.props
@@ -0,0 +1,26 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3<!--
4 Do NOT modify this file. Update the canonical version in Home\repo-template\src\Directory.Build.props
5 then update all of the repos.
6-->
7<Project>
8 <PropertyGroup>
9 <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
10 <EnableSourceLink Condition=" '$(NCrunch)' == '1' ">false</EnableSourceLink>
11
12 <ProjectName Condition=" '$(ProjectName)' == '' ">$(MSBuildProjectName)</ProjectName>
13 <BaseOutputPath>$([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)..\build\))</BaseOutputPath>
14 <BaseIntermediateOutputPath>$(BaseOutputPath)obj\$(ProjectName)\</BaseIntermediateOutputPath>
15 <OutputPath>$(BaseOutputPath)$(Configuration)\</OutputPath>
16
17 <Authors>WiX Toolset Team</Authors>
18 <Company>WiX Toolset</Company>
19 <Copyright>Copyright (c) .NET Foundation and contributors. All rights reserved.</Copyright>
20 <PackageLicenseExpression>MS-RL</PackageLicenseExpression>
21 <Product>WiX Toolset</Product>
22 </PropertyGroup>
23
24 <Import Project="Directory$(MSBuildProjectExtension).props" Condition=" Exists('Directory$(MSBuildProjectExtension).props') " />
25 <Import Project="Custom.Build.props" Condition=" Exists('Custom.Build.props') " />
26</Project>
diff --git a/src/libs/dutil/Directory.Build.targets b/src/libs/dutil/Directory.Build.targets
new file mode 100644
index 00000000..44701fb6
--- /dev/null
+++ b/src/libs/dutil/Directory.Build.targets
@@ -0,0 +1,73 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3<!--
4 Do NOT modify this file. Update the canonical version in Home\repo-template\src\Directory.Build.targets
5 then update all of the repos.
6-->
7<Project>
8 <PropertyGroup>
9 <SigningToolFolder>$(BaseOutputPath)obj\.tools</SigningToolFolder>
10 <SigningToolExe>$(SigningToolFolder)\SignClient.exe</SigningToolExe>
11 <SigningFilelist>$(SigningToolFolder)\empty-filelist.txt</SigningFilelist>
12 <SigningConfiguration>$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), signing.json))\signing.json</SigningConfiguration>
13 </PropertyGroup>
14
15 <PropertyGroup>
16 <CreateDocumentation Condition=" '$(CreateDocumentationFile)'!='true' ">false</CreateDocumentation>
17 <DocumentationFile Condition=" '$(CreateDocumentationFile)'=='true' ">$(OutputPath)\$(AssemblyName).xml</DocumentationFile>
18 </PropertyGroup>
19
20 <Target Name="SetNuspecProperties" DependsOnTargets="InitializeSourceControlInformation" AfterTargets="GetBuildVersion"
21 Condition=" Exists('$(MSBuildProjectName).nuspec') ">
22 <PropertyGroup>
23 <ProjectUrl Condition=" '$(ProjectUrl)'=='' and '$(PrivateRepositoryUrl)'!='' ">$(PrivateRepositoryUrl.Replace('.git',''))</ProjectUrl>
24
25 <NuspecFile>$(MSBuildProjectName).nuspec</NuspecFile>
26 <NuspecBasePath Condition=" '$(NuspecBasePath)'=='' ">$(MSBuildProjectDirectory)</NuspecBasePath>
27 <NuspecProperties>$(NuspecProperties);Id=$(PackageId);Authors="$(Authors)";Configuration=$(Configuration);Copyright="$(Copyright)";Description="$(Description)";Title="$(Title)"</NuspecProperties>
28 <NuspecProperties>$(NuspecProperties);Version=$(NPMPackageVersion);RepositoryCommit=$(SourceRevisionId);RepositoryType=$(RepositoryType);RepositoryUrl=$(PrivateRepositoryUrl);ProjectFolder=$(MSBuildProjectDirectory)\;ProjectUrl=$(ProjectUrl)</NuspecProperties>
29 <PublishRepositoryUrl>true</PublishRepositoryUrl>
30 <SymbolPackageFormat>snupkg</SymbolPackageFormat>
31 </PropertyGroup>
32 </Target>
33
34 <Target Name="PackNative" DependsOnTargets="GetBuildVersion;SetNuspecProperties"
35 Condition=" Exists('$(MSBuildProjectName).nuspec') ">
36
37 <Exec Command='nuget pack $(NuspecFile) -OutputDirectory "$(BaseOutputPath)$(Configuration)" -BasePath "$(NuspecBasePath)" -Properties $(NuspecProperties)'
38 WorkingDirectory="$(MSBuildProjectDirectory)" />
39
40 <ItemGroup>
41 <NuGetPackOutput Include="$(BaseOutputPath)$(Configuration)\**\$(PackageId)*.nupkg" />
42 </ItemGroup>
43 </Target>
44
45 <Target Name="_GetSignClient"
46 Condition=" !Exists('$(SigningToolExe)') ">
47
48 <WriteLinesToFile File='$(SigningFilelist)' Lines='do-not-sign-files-in-nupkg' Overwrite='true' />
49
50 <Exec Command='dotnet.exe tool install --tool-path "$(SigningToolFolder)" SignClient' />
51 </Target>
52
53 <Target Name="SignOutput" DependsOnTargets="_GetSignClient" AfterTargets="AfterBuild"
54 Condition=" '$(SigningUser)'!='' and '$(SignOutput)'!='false' and
55 ('$(MSBuildProjectExtension)'=='.csproj' or ('$(MSBuildProjectExtension)'=='.vcxproj' and '$(ConfigurationType)'!='StaticLibrary'))">
56
57 <Exec Command='"$(SigningToolExe)" sign -i $(TargetPath) -c "$(SigningConfiguration)" -n "WiX Toolset" -d "WiX Toolset" -u https://wixtoolset.org/ -r "$(SigningUser)" -s "$(SigningSecret)"'
58 WorkingDirectory="$(MSBuildProjectDirectory)" EchoOff="true" />
59 </Target>
60
61 <Target Name="SignNupkg" DependsOnTargets="_GetSignClient" AfterTargets="Pack;PackNative"
62 Condition=" '$(SigningUser)'!='' and '@(NuGetPackOutput)'!='' and '$(SignNupkg)'!='false' ">
63 <ItemGroup>
64 <SigningNupkgs Include="@(NuGetPackOutput)" Condition=" '%(Extension)'=='.nupkg' " />
65 </ItemGroup>
66
67 <Exec Command='"$(SigningToolExe)" sign -i "@(SigningNupkgs->&apos;%(Identity)&apos;)" -c "$(SigningConfiguration)" -f "$(SigningFilelist)" -n "WiX Toolset" -d "WiX Toolset" -u https://wixtoolset.org/ -r "$(SigningUser)" -s "$(SigningSecret)"'
68 WorkingDirectory="$(MSBuildProjectDirectory)" EchoOff="true" />
69 </Target>
70
71 <Import Project="Directory$(MSBuildProjectExtension).targets" Condition=" Exists('Directory$(MSBuildProjectExtension).targets') " />
72 <Import Project="Custom.Build.targets" Condition=" Exists('Custom.Build.targets') " />
73</Project>
diff --git a/src/libs/dutil/Directory.csproj.props b/src/libs/dutil/Directory.csproj.props
new file mode 100644
index 00000000..81d24ad1
--- /dev/null
+++ b/src/libs/dutil/Directory.csproj.props
@@ -0,0 +1,13 @@
1<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
2<!--
3 Do NOT modify this file. Update the canonical version in Home\repo-template\src\CSharp.Build.props
4 then update all of the repos.
5-->
6<Project>
7 <PropertyGroup>
8 <CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
9 <SignAssembly>true</SignAssembly>
10 <AssemblyOriginatorKeyFile>$([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)wix.snk))</AssemblyOriginatorKeyFile>
11 <NBGV_EmitThisAssemblyClass>false</NBGV_EmitThisAssemblyClass>
12 </PropertyGroup>
13</Project>
diff --git a/src/libs/dutil/Directory.vcxproj.props b/src/libs/dutil/Directory.vcxproj.props
new file mode 100644
index 00000000..9ea7071b
--- /dev/null
+++ b/src/libs/dutil/Directory.vcxproj.props
@@ -0,0 +1,115 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3
4<Project>
5 <PropertyGroup>
6 <Platform Condition=" '$(Platform)' == '' OR '$(Platform)' == 'AnyCPU' ">Win32</Platform>
7 <IntDir>$(BaseIntermediateOutputPath)$(Configuration)\$(Platform)\</IntDir>
8 <OutDir>$(OutputPath)$(Platform)\</OutDir>
9
10 <!-- NBGV properties -->
11 <AssemblyCompany>$(Company)</AssemblyCompany>
12 <AssemblyCopyright>$(Copyright)</AssemblyCopyright>
13
14 <RuntimeIdentifiers>win-x86;win-x64;win-arm64</RuntimeIdentifiers>
15 <NuGetTargetMoniker>native,Version=v0.0</NuGetTargetMoniker>
16 </PropertyGroup>
17
18 <PropertyGroup Condition="'$(WindowsTargetPlatformVersion)'=='' AND '$(VisualStudioVersion)'>='15.0'">
19 <WindowsTargetPlatformVersion>$([Microsoft.Build.Utilities.ToolLocationHelper]::GetLatestSDKTargetPlatformVersion('Windows', '10.0'))</WindowsTargetPlatformVersion>
20 </PropertyGroup>
21
22 <PropertyGroup>
23 <CodeAnalysisRuleSet Condition=" Exists('$(MSBuildThisFileDirectory)CustomizedNativeRecommendedRules.ruleset') ">$(MSBuildThisFileDirectory)CustomizedNativeRecommendedRules.ruleset</CodeAnalysisRuleSet>
24 </PropertyGroup>
25
26 <ItemDefinitionGroup>
27 <ClCompile>
28 <DisableSpecificWarnings>$(DisableSpecificCompilerWarnings)</DisableSpecificWarnings>
29 <WarningLevel>Level4</WarningLevel>
30 <AdditionalIncludeDirectories>$(ProjectDir)inc;$(MSBuildProjectDirectory);$(IntDir);$(SqlCESdkIncludePath);$(ProjectAdditionalIncludeDirectories);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
31 <PreprocessorDefinitions>WIN32;_WINDOWS;_WIN32_MSI=500;_WIN32_WINNT=0x0501;$(ArmPreprocessorDefinitions);$(UnicodePreprocessorDefinitions);_CRT_STDIO_LEGACY_WIDE_SPECIFIERS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
32 <PrecompiledHeader>Use</PrecompiledHeader>
33 <PrecompiledHeaderFile>precomp.h</PrecompiledHeaderFile>
34 <CallingConvention Condition="'$(Platform)'=='Win32'">StdCall</CallingConvention>
35 <TreatWarningAsError>true</TreatWarningAsError>
36 <ExceptionHandling>false</ExceptionHandling>
37 <AdditionalOptions>-YlprecompDefine</AdditionalOptions>
38 <AdditionalOptions Condition=" $(PlatformToolset.StartsWith('v14')) ">/Zc:threadSafeInit- %(AdditionalOptions)</AdditionalOptions>
39 <MultiProcessorCompilation Condition=" $(NUMBER_OF_PROCESSORS) &gt; 4 ">true</MultiProcessorCompilation>
40 </ClCompile>
41 <ResourceCompile>
42 <PreprocessorDefinitions>$(ArmPreprocessorDefinitions);%(PreprocessorDefinitions)</PreprocessorDefinitions>
43 <AdditionalIncludeDirectories>$(ProjectAdditionalResourceIncludeDirectories);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
44 </ResourceCompile>
45 <Lib>
46 <AdditionalLibraryDirectories>$(OutDir);$(AdditionalMultiTargetLibraryPath);$(ProjectAdditionalLibraryDirectories);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
47 </Lib>
48 <Link>
49 <SubSystem>$(ProjectSubSystem)</SubSystem>
50 <ModuleDefinitionFile>$(ProjectModuleDefinitionFile)</ModuleDefinitionFile>
51 <NoEntryPoint>$(ResourceOnlyDll)</NoEntryPoint>
52 <GenerateDebugInformation>true</GenerateDebugInformation>
53 <AdditionalDependencies>$(ProjectAdditionalLinkLibraries);advapi32.lib;comdlg32.lib;user32.lib;oleaut32.lib;gdi32.lib;shell32.lib;ole32.lib;version.lib;%(AdditionalDependencies)</AdditionalDependencies>
54 <AdditionalLibraryDirectories>$(OutDir);$(AdditionalMultiTargetLibraryPath);$(ArmLibraryDirectories);$(ProjectAdditionalLinkLibraryDirectories);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
55 <AdditionalOptions Condition=" $(PlatformToolset.StartsWith('v14')) ">/IGNORE:4099 %(AdditionalOptions)</AdditionalOptions>
56 </Link>
57 </ItemDefinitionGroup>
58
59 <ItemDefinitionGroup Condition=" '$(Platform)'=='Win32' and '$(PlatformToolset)'!='v100'">
60 <ClCompile>
61 <EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
62 </ClCompile>
63 </ItemDefinitionGroup>
64 <ItemDefinitionGroup Condition=" '$(Platform)'=='arm' ">
65 <ClCompile>
66 <CallingConvention>CDecl</CallingConvention>
67 </ClCompile>
68 </ItemDefinitionGroup>
69 <ItemDefinitionGroup Condition=" '$(ConfigurationType)'=='StaticLibrary' ">
70 <ClCompile>
71 <DebugInformationFormat>OldStyle</DebugInformationFormat>
72 <OmitDefaultLibName>true</OmitDefaultLibName>
73 <IgnoreAllDefaultLibraries>true</IgnoreAllDefaultLibraries>
74 </ClCompile>
75 </ItemDefinitionGroup>
76 <ItemDefinitionGroup Condition=" '$(Configuration)'=='Debug' ">
77 <ClCompile>
78 <Optimization>Disabled</Optimization>
79 <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
80 <PreprocessorDefinitions>_DEBUG;DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
81 <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
82 </ClCompile>
83 </ItemDefinitionGroup>
84 <ItemDefinitionGroup Condition=" '$(Configuration)'=='Debug' and '$(CLRSupport)'=='true' ">
85 <ClCompile>
86 <BasicRuntimeChecks></BasicRuntimeChecks>
87 <RuntimeLibrary>MultiThreadedDebugDll</RuntimeLibrary>
88 </ClCompile>
89 </ItemDefinitionGroup>
90 <ItemDefinitionGroup Condition=" '$(Configuration)'=='Release' ">
91 <ClCompile>
92 <Optimization>MinSpace</Optimization>
93 <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
94 <FunctionLevelLinking>true</FunctionLevelLinking>
95 <IntrinsicFunctions>true</IntrinsicFunctions>
96 <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
97 </ClCompile>
98 <Link>
99 <EnableCOMDATFolding>true</EnableCOMDATFolding>
100 <OptimizeReferences>true</OptimizeReferences>
101 </Link>
102 </ItemDefinitionGroup>
103 <ItemDefinitionGroup Condition=" '$(Configuration)'=='Release' and '$(CLRSupport)'=='true' ">
104 <ClCompile>
105 <BasicRuntimeChecks></BasicRuntimeChecks>
106 <RuntimeLibrary>MultiThreadedDll</RuntimeLibrary>
107 </ClCompile>
108 </ItemDefinitionGroup>
109 <ItemDefinitionGroup Condition=" '$(CLRSupport)'=='true' ">
110 <Link>
111 <KeyFile>$(LinkKeyFile)</KeyFile>
112 <DelaySign>$(LinkDelaySign)</DelaySign>
113 </Link>
114 </ItemDefinitionGroup>
115</Project>
diff --git a/src/libs/dutil/NativeMultiTargeting.Build.props b/src/libs/dutil/NativeMultiTargeting.Build.props
new file mode 100644
index 00000000..1ff46559
--- /dev/null
+++ b/src/libs/dutil/NativeMultiTargeting.Build.props
@@ -0,0 +1,10 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3
4<Project>
5 <!-- Overrides the standard Cpp.Build.props to include the PlatformToolset in the output path. -->
6 <PropertyGroup>
7 <IntDir>$(BaseIntermediateOutputPath)$(Configuration)\$(PlatformToolset)\$(PlatformTarget)\</IntDir>
8 <OutDir>$(OutputPath)$(PlatformToolset)\$(PlatformTarget)\</OutDir>
9 </PropertyGroup>
10</Project>
diff --git a/src/libs/dutil/README.md b/src/libs/dutil/README.md
new file mode 100644
index 00000000..2d6605fe
--- /dev/null
+++ b/src/libs/dutil/README.md
@@ -0,0 +1,2 @@
1# dutil
2dutil.lib - foundation library for all native code in WiX Toolset
diff --git a/src/libs/dutil/WixToolset.DUtil/acl2util.cpp b/src/libs/dutil/WixToolset.DUtil/acl2util.cpp
new file mode 100644
index 00000000..598f12e7
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/acl2util.cpp
@@ -0,0 +1,135 @@
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// Exit macros
6#define AclExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__)
7#define AclExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__)
8#define AclExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__)
9#define AclExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__)
10#define AclExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__)
11#define AclExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__)
12#define AclExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_ACLUTIL, p, x, e, s, __VA_ARGS__)
13#define AclExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_ACLUTIL, p, x, s, __VA_ARGS__)
14#define AclExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_ACLUTIL, p, x, e, s, __VA_ARGS__)
15#define AclExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_ACLUTIL, p, x, s, __VA_ARGS__)
16#define AclExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_ACLUTIL, e, x, s, __VA_ARGS__)
17#define AclExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_ACLUTIL, g, x, s, __VA_ARGS__)
18
19/********************************************************************
20AclCalculateServiceSidString - gets the SID string for the given service name
21
22NOTE: psczSid should be freed with StrFree()
23********************************************************************/
24extern "C" HRESULT DAPI AclCalculateServiceSidString(
25 __in LPCWSTR wzServiceName,
26 __in SIZE_T cchServiceName,
27 __deref_out_z LPWSTR* psczSid
28 )
29{
30 // TODO: use undocumented RtlCreateServiceSid function?
31 // http://blogs.technet.com/b/voy/archive/2007/03/22/per-service-sid.aspx
32 // Assume little endian.
33 HRESULT hr = S_OK;
34 LPWSTR sczUpperServiceName = NULL;
35 DWORD cbHash = SHA1_HASH_LEN;
36 BYTE* pbHash = NULL;
37
38 Assert(psczSid);
39
40 if (0 == cchServiceName)
41 {
42 hr = ::StringCchLengthW(wzServiceName, STRSAFE_MAX_CCH, reinterpret_cast<size_t*>(&cchServiceName));
43 AclExitOnFailure(hr, "Failed to get the length of the service name.");
44 }
45
46 hr = StrAllocStringToUpperInvariant(&sczUpperServiceName, wzServiceName, cchServiceName);
47 AclExitOnFailure(hr, "Failed to upper case the service name.");
48
49 pbHash = reinterpret_cast<BYTE*>(MemAlloc(cbHash, TRUE));
50 AclExitOnNull(pbHash, hr, E_OUTOFMEMORY, "Failed to allocate hash byte array.");
51
52 hr = CrypHashBuffer(reinterpret_cast<BYTE*>(sczUpperServiceName), cchServiceName * sizeof(WCHAR), PROV_RSA_FULL, CALG_SHA1, pbHash, cbHash);
53 AclExitOnNull(pbHash, hr, E_OUTOFMEMORY, "Failed to hash the service name.");
54
55 hr = StrAllocFormatted(psczSid, L"S-1-5-80-%u-%u-%u-%u-%u",
56 MAKEDWORD(MAKEWORD(pbHash[0], pbHash[1]), MAKEWORD(pbHash[2], pbHash[3])),
57 MAKEDWORD(MAKEWORD(pbHash[4], pbHash[5]), MAKEWORD(pbHash[6], pbHash[7])),
58 MAKEDWORD(MAKEWORD(pbHash[8], pbHash[9]), MAKEWORD(pbHash[10], pbHash[11])),
59 MAKEDWORD(MAKEWORD(pbHash[12], pbHash[13]), MAKEWORD(pbHash[14], pbHash[15])),
60 MAKEDWORD(MAKEWORD(pbHash[16], pbHash[17]), MAKEWORD(pbHash[18], pbHash[19])));
61
62LExit:
63 ReleaseMem(pbHash);
64 ReleaseStr(sczUpperServiceName);
65
66 return hr;
67}
68
69
70/********************************************************************
71AclGetAccountSidStringEx - gets a string version of the account's SID
72 calculates a service's SID if lookup fails
73
74NOTE: psczSid should be freed with StrFree()
75********************************************************************/
76extern "C" HRESULT DAPI AclGetAccountSidStringEx(
77 __in_z LPCWSTR wzSystem,
78 __in_z LPCWSTR wzAccount,
79 __deref_out_z LPWSTR* psczSid
80 )
81{
82 HRESULT hr = S_OK;
83 SIZE_T cchAccount = 0;
84 PSID psid = NULL;
85 LPWSTR pwz = NULL;
86 LPWSTR sczSid = NULL;
87
88 Assert(psczSid);
89
90 hr = AclGetAccountSid(wzSystem, wzAccount, &psid);
91 if (SUCCEEDED(hr))
92 {
93 Assert(::IsValidSid(psid));
94
95 if (!::ConvertSidToStringSidW(psid, &pwz))
96 {
97 AclExitWithLastError(hr, "Failed to convert SID to string for Account: %ls", wzAccount);
98 }
99
100 hr = StrAllocString(psczSid, pwz, 0);
101 }
102 else
103 {
104 if (HRESULT_FROM_WIN32(ERROR_NONE_MAPPED) == hr)
105 {
106 HRESULT hrLength = ::StringCchLengthW(wzAccount, STRSAFE_MAX_CCH, reinterpret_cast<size_t*>(&cchAccount));
107 AclExitOnFailure(hrLength, "Failed to get the length of the account name.");
108
109 if (11 < cchAccount && CSTR_EQUAL == CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, L"NT SERVICE\\", 11, wzAccount, 11))
110 {
111 // If the service is not installed then LookupAccountName doesn't resolve the SID, but we can calculate it.
112 LPCWSTR wzServiceName = &wzAccount[11];
113 hr = AclCalculateServiceSidString(wzServiceName, cchAccount - 11, &sczSid);
114 AclExitOnFailure(hr, "Failed to calculate the service SID for %ls", wzServiceName);
115
116 *psczSid = sczSid;
117 sczSid = NULL;
118 }
119 }
120 AclExitOnFailure(hr, "Failed to get SID for account: %ls", wzAccount);
121 }
122
123LExit:
124 ReleaseStr(sczSid);
125 if (pwz)
126 {
127 ::LocalFree(pwz);
128 }
129 if (psid)
130 {
131 AclFreeSid(psid);
132 }
133
134 return hr;
135}
diff --git a/src/libs/dutil/WixToolset.DUtil/aclutil.cpp b/src/libs/dutil/WixToolset.DUtil/aclutil.cpp
new file mode 100644
index 00000000..c9733033
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/aclutil.cpp
@@ -0,0 +1,1044 @@
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// Exit macros
6#define AclExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__)
7#define AclExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__)
8#define AclExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__)
9#define AclExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__)
10#define AclExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__)
11#define AclExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_ACLUTIL, x, s, __VA_ARGS__)
12#define AclExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_ACLUTIL, p, x, e, s, __VA_ARGS__)
13#define AclExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_ACLUTIL, p, x, s, __VA_ARGS__)
14#define AclExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_ACLUTIL, p, x, e, s, __VA_ARGS__)
15#define AclExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_ACLUTIL, p, x, s, __VA_ARGS__)
16#define AclExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_ACLUTIL, e, x, s, __VA_ARGS__)
17#define AclExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_ACLUTIL, g, x, s, __VA_ARGS__)
18
19/********************************************************************
20AclCheckAccess - determines if token has appropriate privileges
21
22NOTE: paa->fDenyAccess and paa->dwAccessMask are ignored and must be zero
23if hToken is NULL, the thread will be checked
24if hToken is not NULL the token must be an impersonation token
25********************************************************************/
26extern "C" HRESULT DAPI AclCheckAccess(
27 __in HANDLE hToken,
28 __in ACL_ACCESS* paa
29 )
30{
31 HRESULT hr = S_OK;
32 PSID psid = NULL;
33 BOOL fIsMember = FALSE;
34
35 AclExitOnNull(paa, hr, E_INVALIDARG, "Failed to check ACL access, because no acl access provided to check");
36 Assert(0 == paa->fDenyAccess && 0 == paa->dwAccessMask);
37
38 if (paa->pwzAccountName)
39 {
40 hr = AclGetAccountSid(NULL, paa->pwzAccountName, &psid);
41 AclExitOnFailure(hr, "failed to get SID for account: %ls", paa->pwzAccountName);
42 }
43 else
44 {
45 if (!::AllocateAndInitializeSid(&paa->sia, paa->nSubAuthorityCount, paa->nSubAuthority[0], paa->nSubAuthority[1], paa->nSubAuthority[2], paa->nSubAuthority[3], paa->nSubAuthority[4], paa->nSubAuthority[5], paa->nSubAuthority[6], paa->nSubAuthority[7], &psid))
46 {
47 AclExitWithLastError(hr, "failed to initialize SID");
48 }
49 }
50
51 if (!::CheckTokenMembership(hToken, psid, &fIsMember))
52 {
53 AclExitWithLastError(hr, "failed to check membership");
54 }
55
56 fIsMember ? hr = S_OK : hr = S_FALSE;
57
58LExit:
59 if (psid)
60 {
61 ::FreeSid(psid); // TODO: does this have bad behavior if SID was allocated by Heap from AclGetAccountSid?
62 }
63
64 return hr;
65}
66
67
68/********************************************************************
69AclCheckAdministratorAccess - determines if token has Administrator privileges
70
71NOTE: if hToken is NULL, the thread will be checked
72if hToken is not NULL the token must be an impersonation token
73********************************************************************/
74extern "C" HRESULT DAPI AclCheckAdministratorAccess(
75 __in HANDLE hToken
76 )
77{
78 ACL_ACCESS aa;
79 SID_IDENTIFIER_AUTHORITY siaNt = SECURITY_NT_AUTHORITY;
80
81 memset(&aa, 0, sizeof(aa));
82 aa.sia = siaNt;
83 aa.nSubAuthorityCount = 2;
84 aa.nSubAuthority[0] = SECURITY_BUILTIN_DOMAIN_RID;
85 aa.nSubAuthority[1] = DOMAIN_ALIAS_RID_ADMINS;
86
87 return AclCheckAccess(hToken, &aa);
88}
89
90
91/********************************************************************
92AclCheckLocalSystemAccess - determines if token has LocalSystem privileges
93
94NOTE: if hToken is NULL, the thread will be checked
95if hToken is not NULL the token must be an impersonation token
96********************************************************************/
97extern "C" HRESULT DAPI AclCheckLocalSystemAccess(
98 __in HANDLE hToken
99 )
100{
101 ACL_ACCESS aa;
102 SID_IDENTIFIER_AUTHORITY siaNt = SECURITY_NT_AUTHORITY;
103
104 memset(&aa, 0, sizeof(aa));
105 aa.sia = siaNt;
106 aa.nSubAuthorityCount = 1;
107 aa.nSubAuthority[0] = SECURITY_LOCAL_SYSTEM_RID;
108
109 return AclCheckAccess(hToken, &aa);
110}
111
112
113/********************************************************************
114AclGetWellKnownSid - returns a SID for the specified account
115
116********************************************************************/
117extern "C" HRESULT DAPI AclGetWellKnownSid(
118 __in WELL_KNOWN_SID_TYPE wkst,
119 __deref_out PSID* ppsid
120 )
121{
122 Assert(ppsid);
123
124 HRESULT hr = S_OK;;
125 PSID psid = NULL;
126 DWORD cbSid = SECURITY_MAX_SID_SIZE;
127
128 PSID psidTemp = NULL;
129#if(_WIN32_WINNT < 0x0501)
130 SID_IDENTIFIER_AUTHORITY siaNT = SECURITY_NT_AUTHORITY;
131 SID_IDENTIFIER_AUTHORITY siaWorld = SECURITY_WORLD_SID_AUTHORITY;
132 SID_IDENTIFIER_AUTHORITY siaCreator = SECURITY_CREATOR_SID_AUTHORITY;
133 BOOL fSuccess = FALSE;
134#endif
135
136 //
137 // allocate memory for the SID and get it
138 //
139 psid = static_cast<PSID>(MemAlloc(cbSid, TRUE));
140 AclExitOnNull(psid, hr, E_OUTOFMEMORY, "failed allocate memory for well known SID");
141
142#if(_WIN32_WINNT < 0x0501)
143 switch (wkst)
144 {
145 case WinWorldSid: // Everyone
146 fSuccess = ::AllocateAndInitializeSid(&siaWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp);
147 break;
148 case WinAuthenticatedUserSid: // Authenticated Users
149 fSuccess = ::AllocateAndInitializeSid(&siaNT, 1, SECURITY_AUTHENTICATED_USER_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp);
150 break;
151 case WinLocalSystemSid: // LocalSystem
152 fSuccess = ::AllocateAndInitializeSid(&siaNT, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp);
153 break;
154 case WinLocalServiceSid: // LocalService
155 fSuccess = ::AllocateAndInitializeSid(&siaNT, 1, SECURITY_LOCAL_SERVICE_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp);
156 break;
157 case WinNetworkServiceSid: // NetworkService
158 fSuccess = ::AllocateAndInitializeSid(&siaNT, 1, SECURITY_NETWORK_SERVICE_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp);
159 break;
160 case WinBuiltinGuestsSid: // Guests
161 fSuccess = ::AllocateAndInitializeSid(&siaNT, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_GUESTS, 0, 0, 0, 0, 0, 0, &psidTemp);
162 break;
163 case WinBuiltinAdministratorsSid: // Administrators
164 fSuccess = ::AllocateAndInitializeSid(&siaNT, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &psidTemp);
165 break;
166 case WinBuiltinUsersSid: // Users
167 fSuccess = ::AllocateAndInitializeSid(&siaNT, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_USERS, 0, 0, 0, 0, 0, 0, &psidTemp);
168 break;
169 case WinCreatorOwnerSid: //CREATOR OWNER
170 fSuccess = ::AllocateAndInitializeSid(&siaCreator, 1, SECURITY_CREATOR_OWNER_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp);
171 break;
172 case WinInteractiveSid: // INTERACTIVE
173 fSuccess = ::AllocateAndInitializeSid(&siaNT, 1, SECURITY_INTERACTIVE_RID, 0, 0, 0, 0, 0, 0, 0, &psidTemp);
174 break;
175 default:
176 hr = E_INVALIDARG;
177 AclExitOnFailure(hr, "unknown well known SID: %d", wkst);
178 }
179
180 if (!fSuccess)
181 AclExitOnLastError(hr, "failed to allocate well known SID: %d", wkst);
182
183 if (!::CopySid(cbSid, psid, psidTemp))
184 AclExitOnLastError(hr, "failed to create well known SID: %d", wkst);
185#else
186 Assert(NULL == psidTemp);
187 if (!::CreateWellKnownSid(wkst, NULL, psid, &cbSid))
188 {
189 AclExitWithLastError(hr, "failed to create well known SID: %d", wkst);
190 }
191#endif
192
193 *ppsid = psid;
194 psid = NULL; // null it here so it won't be released below
195
196 Assert(S_OK == hr && ::IsValidSid(*ppsid));
197LExit:
198 if (psidTemp)
199 {
200 ::FreeSid(psidTemp);
201 }
202
203 ReleaseMem(psid);
204
205 return hr;
206}
207
208
209/********************************************************************
210AclGetAccountSid - returns a SID for the specified account
211
212********************************************************************/
213extern "C" HRESULT DAPI AclGetAccountSid(
214 __in_opt LPCWSTR wzSystem,
215 __in_z LPCWSTR wzAccount,
216 __deref_out PSID* ppsid
217 )
218{
219 Assert(wzAccount && *wzAccount && ppsid);
220
221 HRESULT hr = S_OK;
222 UINT er = ERROR_SUCCESS;
223 PSID psid = NULL;
224 DWORD cbSid = SECURITY_MAX_SID_SIZE;
225 LPWSTR pwzDomainName = NULL;
226 DWORD cbDomainName = 255;
227 SID_NAME_USE peUse;
228
229 //
230 // allocate memory for the SID and domain name
231 //
232 psid = static_cast<PSID>(MemAlloc(cbSid, TRUE));
233 AclExitOnNull(psid, hr, E_OUTOFMEMORY, "failed to allocate memory for SID");
234 hr = StrAlloc(&pwzDomainName, cbDomainName);
235 AclExitOnFailure(hr, "failed to allocate string for domain name");
236
237 //
238 // try to lookup the account now
239 //
240 if (!::LookupAccountNameW(wzSystem, wzAccount, psid, &cbSid, pwzDomainName, &cbDomainName, &peUse))
241 {
242 // if one of the buffers wasn't large enough
243 er = ::GetLastError();
244 if (ERROR_INSUFFICIENT_BUFFER == er)
245 {
246 if (SECURITY_MAX_SID_SIZE < cbSid)
247 {
248 PSID psidNew = static_cast<PSID>(MemReAlloc(psid, cbSid, TRUE));
249 AclExitOnNullWithLastError(psidNew, hr, "failed to allocate memory for account: %ls", wzAccount);
250
251 psid = psidNew;
252 }
253 if (255 < cbDomainName)
254 {
255 hr = StrAlloc(&pwzDomainName, cbDomainName);
256 AclExitOnFailure(hr, "failed to allocate string for domain name");
257 }
258
259 if (!::LookupAccountNameW(wzSystem, wzAccount, psid, &cbSid, pwzDomainName, &cbDomainName, &peUse))
260 {
261 AclExitWithLastError(hr, "failed to lookup account: %ls", wzAccount);
262 }
263 }
264 else
265 {
266 AclExitOnWin32Error(er, hr, "failed to lookup account: %ls", wzAccount);
267 }
268 }
269
270 *ppsid = psid;
271 psid = NULL;
272
273 hr = S_OK;
274LExit:
275 ReleaseStr(pwzDomainName);
276 ReleaseMem(psid);
277
278 return hr;
279}
280
281
282/********************************************************************
283AclGetAccountSidString - gets a string version of the user's SID
284
285NOTE: ppwzSid should be freed with StrFree()
286********************************************************************/
287extern "C" HRESULT DAPI AclGetAccountSidString(
288 __in_z LPCWSTR wzSystem,
289 __in_z LPCWSTR wzAccount,
290 __deref_out_z LPWSTR* ppwzSid
291 )
292{
293 Assert(ppwzSid);
294 HRESULT hr = S_OK;
295 PSID psid = NULL;
296 LPWSTR pwz = NULL;
297
298 *ppwzSid = NULL;
299
300 hr = AclGetAccountSid(wzSystem, wzAccount, &psid);
301 AclExitOnFailure(hr, "failed to get SID for account: %ls", wzAccount);
302 Assert(::IsValidSid(psid));
303
304 if (!::ConvertSidToStringSidW(psid, &pwz))
305 {
306 AclExitWithLastError(hr, "failed to convert SID to string for Account: %ls", wzAccount);
307 }
308
309 hr = StrAllocString(ppwzSid, pwz, 0);
310
311LExit:
312 if (FAILED(hr))
313 {
314 ReleaseNullStr(*ppwzSid);
315 }
316
317 if (pwz)
318 {
319 ::LocalFree(pwz);
320 }
321
322 if (psid)
323 {
324 AclFreeSid(psid);
325 }
326
327 return hr;
328}
329
330
331/********************************************************************
332AclCreateDacl - creates a DACL from ACL_ACE structures
333
334********************************************************************/
335extern "C" HRESULT DAPI AclCreateDacl(
336 __in_ecount(cDeny) ACL_ACE rgaaDeny[],
337 __in DWORD cDeny,
338 __in_ecount(cAllow) ACL_ACE rgaaAllow[],
339 __in DWORD cAllow,
340 __deref_out ACL** ppAcl
341 )
342{
343 Assert(ppAcl);
344 HRESULT hr = S_OK;
345 ACL* pAcl = NULL;
346 DWORD cbAcl = 0;
347 DWORD i;
348
349 *ppAcl = NULL;
350
351 // initialize the ACL
352 cbAcl = sizeof(ACL);
353 for (i = 0; i < cDeny; ++i)
354 {
355 cbAcl += sizeof(ACCESS_DENIED_ACE) + ::GetLengthSid(rgaaDeny[i].psid) - sizeof(DWORD);
356 }
357
358 for (i = 0; i < cAllow; ++i)
359 {
360 cbAcl += sizeof(ACCESS_ALLOWED_ACE) + ::GetLengthSid(rgaaAllow[i].psid) - sizeof(DWORD);
361 }
362
363 pAcl = static_cast<ACL*>(MemAlloc(cbAcl, TRUE));
364 AclExitOnNull(pAcl, hr, E_OUTOFMEMORY, "failed to allocate ACL");
365
366#pragma prefast(push)
367#pragma prefast(disable:25029)
368 if (!::InitializeAcl(pAcl, cbAcl, ACL_REVISION))
369#pragma prefast(pop)
370 {
371 AclExitWithLastError(hr, "failed to initialize ACL");
372 }
373
374 // add in the ACEs (denied first)
375 for (i = 0; i < cDeny; ++i)
376 {
377#pragma prefast(push)
378#pragma prefast(disable:25029)
379 if (!::AddAccessDeniedAceEx(pAcl, ACL_REVISION, rgaaDeny[i].dwFlags, rgaaDeny[i].dwMask, rgaaDeny[i].psid))
380#pragma prefast(pop)
381 {
382 AclExitWithLastError(hr, "failed to add access denied ACE #%d to ACL", i);
383 }
384 }
385 for (i = 0; i < cAllow; ++i)
386 {
387#pragma prefast(push)
388#pragma prefast(disable:25029)
389 if (!::AddAccessAllowedAceEx(pAcl, ACL_REVISION, rgaaAllow[i].dwFlags, rgaaAllow[i].dwMask, rgaaAllow[i].psid))
390#pragma prefast(pop)
391 {
392 AclExitWithLastError(hr, "failed to add access allowed ACE #%d to ACL", i);
393 }
394 }
395
396 *ppAcl = pAcl;
397 pAcl = NULL;
398 AssertSz(::IsValidAcl(*ppAcl), "AclCreateDacl() - created invalid ACL");
399 Assert(S_OK == hr);
400LExit:
401 if (pAcl)
402 {
403 AclFreeDacl(pAcl);
404 }
405
406 return hr;
407}
408
409
410/********************************************************************
411AclAddToDacl - creates a new DACL from an ACL plus new ACL_ACE structure
412
413********************************************************************/
414extern "C" HRESULT DAPI AclAddToDacl(
415 __in ACL* pAcl,
416 __in_ecount_opt(cDeny) const ACL_ACE rgaaDeny[],
417 __in DWORD cDeny,
418 __in_ecount_opt(cAllow) const ACL_ACE rgaaAllow[],
419 __in DWORD cAllow,
420 __deref_out ACL** ppAclNew
421 )
422{
423 Assert(pAcl && ::IsValidAcl(pAcl) && ppAclNew);
424 HRESULT hr = S_OK;
425
426 ACL_SIZE_INFORMATION asi;
427 ACL_ACE* paaNewDeny = NULL;
428 DWORD cNewDeny = 0;
429 ACL_ACE* paaNewAllow = NULL;
430 DWORD cNewAllow = 0;
431
432 ACCESS_ALLOWED_ACE* paaa;
433 ACCESS_DENIED_ACE* pada;
434 DWORD i;
435
436 // allocate memory for all the new ACEs (NOTE: this over calculates the memory necessary, but that's okay)
437 if (!::GetAclInformation(pAcl, &asi, sizeof(asi), AclSizeInformation))
438 {
439 AclExitWithLastError(hr, "failed to get information about original ACL");
440 }
441
442 if ((asi.AceCount + cDeny) < asi.AceCount || // check for overflow
443 (asi.AceCount + cDeny) < cDeny || // check for overflow
444 (asi.AceCount + cDeny) >= MAXSIZE_T / sizeof(ACL_ACE))
445 {
446 hr = E_OUTOFMEMORY;
447 AclExitOnFailure(hr, "Not enough memory to allocate %d ACEs", (asi.AceCount + cDeny));
448 }
449
450 paaNewDeny = static_cast<ACL_ACE*>(MemAlloc(sizeof(ACL_ACE) * (asi.AceCount + cDeny), TRUE));
451 AclExitOnNull(paaNewDeny, hr, E_OUTOFMEMORY, "failed to allocate memory for new deny ACEs");
452
453 if ((asi.AceCount + cAllow) < asi.AceCount || // check for overflow
454 (asi.AceCount + cAllow) < cAllow || // check for overflow
455 (asi.AceCount + cAllow) >= MAXSIZE_T / sizeof(ACL_ACE))
456 {
457 hr = E_OUTOFMEMORY;
458 AclExitOnFailure(hr, "Not enough memory to allocate %d ACEs", (asi.AceCount + cAllow));
459 }
460
461 paaNewAllow = static_cast<ACL_ACE*>(MemAlloc(sizeof(ACL_ACE) * (asi.AceCount + cAllow), TRUE));
462 AclExitOnNull(paaNewAllow, hr, E_OUTOFMEMORY, "failed to allocate memory for new allow ACEs");
463
464 // fill in the new structures with old data then new data (denied first)
465 for (i = 0; i < asi.AceCount; ++i)
466 {
467 if (!::GetAce(pAcl, i, reinterpret_cast<LPVOID*>(&pada)))
468 {
469 AclExitWithLastError(hr, "failed to get ACE #%d from ACL", i);
470 }
471
472 if (ACCESS_DENIED_ACE_TYPE != pada->Header.AceType)
473 {
474 continue; // skip non-denied aces
475 }
476
477 paaNewDeny[i].dwFlags = pada->Header.AceFlags;
478 paaNewDeny[i].dwMask = pada->Mask;
479 paaNewDeny[i].psid = reinterpret_cast<PSID>(&(pada->SidStart));
480 ++cNewDeny;
481 }
482
483 memcpy(paaNewDeny + cNewDeny, rgaaDeny, sizeof(ACL_ACE) * cDeny);
484 cNewDeny += cDeny;
485
486
487 for (i = 0; i < asi.AceCount; ++i)
488 {
489 if (!::GetAce(pAcl, i, reinterpret_cast<LPVOID*>(&paaa)))
490 {
491 AclExitWithLastError(hr, "failed to get ACE #%d from ACL", i);
492 }
493
494 if (ACCESS_ALLOWED_ACE_TYPE != paaa->Header.AceType)
495 {
496 continue; // skip non-allowed aces
497 }
498
499 paaNewAllow[i].dwFlags = paaa->Header.AceFlags;
500 paaNewAllow[i].dwMask = paaa->Mask;
501 paaNewAllow[i].psid = reinterpret_cast<PSID>(&(paaa->SidStart));
502 ++cNewAllow;
503 }
504
505 memcpy(paaNewAllow + cNewAllow, rgaaAllow, sizeof(ACL_ACE) * cAllow);
506 cNewAllow += cAllow;
507
508 // create the dacl with the new
509 hr = AclCreateDacl(paaNewDeny, cNewDeny, paaNewAllow, cNewAllow, ppAclNew);
510 AclExitOnFailure(hr, "failed to create new ACL from existing ACL");
511
512 AssertSz(::IsValidAcl(*ppAclNew), "AclAddToDacl() - created invalid ACL");
513 Assert(S_OK == hr);
514LExit:
515 ReleaseMem(paaNewAllow);
516 ReleaseMem(paaNewDeny);
517
518 return hr;
519}
520
521
522/********************************************************************
523AclMergeDacls - creates a new DACL from two existing ACLs
524
525********************************************************************/
526extern "C" HRESULT DAPI AclMergeDacls(
527 __in const ACL* pAcl1,
528 __in const ACL* pAcl2,
529 __deref_out ACL** ppAclNew
530 )
531{
532 HRESULT hr = E_NOTIMPL;
533
534 Assert(pAcl1 && pAcl2 && ppAclNew);
535 UNREFERENCED_PARAMETER(pAcl1);
536 UNREFERENCED_PARAMETER(pAcl2);
537 UNREFERENCED_PARAMETER(ppAclNew);
538
539//LExit:
540 return hr;
541}
542
543
544/********************************************************************
545AclCreateDaclOld - creates a DACL from an ACL_ACCESS structure
546
547********************************************************************/
548extern "C" HRESULT DAPI AclCreateDaclOld(
549 __in_ecount(cAclAccesses) ACL_ACCESS* paa,
550 __in DWORD cAclAccesses,
551 __deref_out ACL** ppACL
552 )
553{
554 Assert(ppACL);
555 HRESULT hr = S_OK;
556 DWORD* pdwAccessMask = NULL;
557 PSID* ppsid = NULL;
558
559 DWORD i;
560 int cbAcl;
561
562 *ppACL = NULL;
563
564 //
565 // create the SIDs and calculate the space for the ACL
566 //
567 pdwAccessMask = static_cast<DWORD*>(MemAlloc(sizeof(DWORD) * cAclAccesses, TRUE));
568 AclExitOnNull(pdwAccessMask, hr, E_OUTOFMEMORY, "failed allocate memory for access mask");
569 ppsid = static_cast<PSID*>(MemAlloc(sizeof(PSID) * cAclAccesses, TRUE));
570 AclExitOnNull(ppsid, hr, E_OUTOFMEMORY, "failed allocate memory for sid");
571
572 cbAcl = sizeof (ACL); // start with the size of the header
573 for (i = 0; i < cAclAccesses; ++i)
574 {
575 if (paa[i].pwzAccountName)
576 {
577 hr = AclGetAccountSid(NULL, paa[i].pwzAccountName, ppsid + i);
578 AclExitOnFailure(hr, "failed to get SID for account: %ls", paa[i].pwzAccountName);
579 }
580 else
581 {
582 if ((!::AllocateAndInitializeSid(&paa[i].sia, paa[i].nSubAuthorityCount,
583 paa[i].nSubAuthority[0], paa[i].nSubAuthority[1],
584 paa[i].nSubAuthority[2], paa[i].nSubAuthority[3],
585 paa[i].nSubAuthority[4], paa[i].nSubAuthority[5],
586 paa[i].nSubAuthority[6], paa[i].nSubAuthority[7],
587 (void**)(ppsid + i))))
588 {
589 AclExitWithLastError(hr, "failed to initialize SIDs #%u", i);
590 }
591 }
592
593 // add the newly allocated SID size to the count of bytes for this ACL
594 cbAcl +=::GetLengthSid(*(ppsid + i)) - sizeof(DWORD);
595 if (paa[i].fDenyAccess)
596 {
597 cbAcl += sizeof(ACCESS_DENIED_ACE);
598 }
599 else
600 {
601 cbAcl += sizeof(ACCESS_ALLOWED_ACE);
602 }
603
604 pdwAccessMask[i] = paa[i].dwAccessMask;
605 }
606
607 //
608 // allocate the ACL and set the appropriate ACEs
609 //
610 *ppACL = static_cast<ACL*>(MemAlloc(cbAcl, FALSE));
611 AclExitOnNull(*ppACL, hr, E_OUTOFMEMORY, "failed allocate memory for ACL");
612
613#pragma prefast(push)
614#pragma prefast(disable:25029)
615 if (!::InitializeAcl(*ppACL, cbAcl, ACL_REVISION))
616#pragma prefast(pop)
617 {
618 AclExitWithLastError(hr, "failed to initialize ACLs");
619 }
620
621 // add an access-allowed ACE for each of the SIDs
622 for (i = 0; i < cAclAccesses; ++i)
623 {
624 if (paa[i].fDenyAccess)
625 {
626#pragma prefast(push)
627#pragma prefast(disable:25029)
628 if (!::AddAccessDeniedAceEx(*ppACL, ACL_REVISION, CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE, pdwAccessMask[i], *(ppsid + i)))
629#pragma prefast(pop)
630 {
631 AclExitWithLastError(hr, "failed to add access denied for ACE");
632 }
633 }
634 else
635 {
636#pragma prefast(push)
637#pragma prefast(disable:25029)
638 if (!::AddAccessAllowedAceEx(*ppACL, ACL_REVISION, CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE, pdwAccessMask[i], *(ppsid + i)))
639#pragma prefast(pop)
640 {
641 AclExitWithLastError(hr, "failed to add access allowed for ACE");
642 }
643 }
644 }
645
646LExit:
647 if (FAILED(hr))
648 {
649 ReleaseNullMem(*ppACL);
650 }
651
652 if (ppsid)
653 {
654 for (i = 0; i < cAclAccesses; ++i)
655 {
656 if (ppsid[i])
657 {
658 ::FreeSid(ppsid[i]);
659 }
660 }
661
662 MemFree(ppsid);
663 }
664
665 ReleaseMem(pdwAccessMask);
666
667 return hr;
668}
669
670
671/********************************************************************
672AclCreateSecurityDescriptorFromDacl - creates a self-relative security
673descriptor from an existing DACL
674
675********************************************************************/
676extern "C" HRESULT DAPI AclCreateSecurityDescriptorFromDacl(
677 __in ACL* pACL,
678 __deref_out SECURITY_DESCRIPTOR** ppsd
679 )
680{
681 HRESULT hr = S_OK;
682
683 SECURITY_DESCRIPTOR sd;
684 DWORD cbSD;
685
686 AclExitOnNull(pACL, hr, E_INVALIDARG, "Failed to create security descriptor from DACL, because no DACL was provided");
687 AclExitOnNull(ppsd, hr, E_INVALIDARG, "Failed to create security descriptor from DACL, because no output object was provided");
688
689 *ppsd = NULL;
690
691 //
692 // create the absolute security descriptor
693 //
694
695 // initialize our security descriptor, throw the ACL into it, and set the owner
696#pragma prefast(push)
697#pragma prefast(disable:25028) // We only call this when pACL isn't NULL, so this call is safe according to the docs
698#pragma prefast(disable:25029)
699 if (!::InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION) ||
700 (!::SetSecurityDescriptorDacl(&sd, TRUE, pACL, FALSE)) ||
701 (!::SetSecurityDescriptorOwner(&sd, NULL, FALSE)))
702#pragma prefast(pop)
703 {
704 AclExitWithLastError(hr, "failed to initialize security descriptor");
705 }
706
707 //
708 // create the self-relative security descriptor
709 //
710 cbSD = ::GetSecurityDescriptorLength(&sd);
711 *ppsd = static_cast<SECURITY_DESCRIPTOR*>(MemAlloc(cbSD, FALSE));
712 AclExitOnNull(*ppsd, hr, E_OUTOFMEMORY, "failed allocate memory for security descriptor");
713
714 ::MakeSelfRelativeSD(&sd, (BYTE*)*ppsd, &cbSD);
715 Assert(::IsValidSecurityDescriptor(*ppsd));
716
717LExit:
718 if (FAILED(hr) && NULL != ppsd && NULL != *ppsd)
719 {
720 MemFree(*ppsd);
721 *ppsd = NULL;
722 }
723
724 return hr;
725}
726
727
728/********************************************************************
729AclCreateSecurityDescriptor - creates a self-relative security descriptor from an
730ACL_ACCESS structure
731
732NOTE: ppsd should be freed with AclFreeSecurityDescriptor()
733********************************************************************/
734extern "C" HRESULT DAPI AclCreateSecurityDescriptor(
735 __in_ecount(cAclAccesses) ACL_ACCESS* paa,
736 __in DWORD cAclAccesses,
737 __deref_out SECURITY_DESCRIPTOR** ppsd
738 )
739{
740 Assert(ppsd);
741 HRESULT hr = S_OK;
742
743 ACL* pACL;
744
745 *ppsd = NULL;
746
747 //
748 // create the DACL
749 //
750 hr = AclCreateDaclOld(paa, cAclAccesses, &pACL);
751 AclExitOnFailure(hr, "failed to create DACL for security descriptor");
752
753 //
754 // create self-relative security descriptor
755 //
756 hr = AclCreateSecurityDescriptorFromDacl(pACL, ppsd);
757
758LExit:
759 return hr;
760}
761
762
763/********************************************************************
764AclCreateSecurityDescriptorFromString - creates a self-relative security
765descriptor from an SDDL string
766
767NOTE: ppsd should be freed with AclFreeSecurityDescriptor()
768********************************************************************/
769extern "C" HRESULT DAPI AclCreateSecurityDescriptorFromString(
770 __deref_out SECURITY_DESCRIPTOR** ppsd,
771 __in_z __format_string LPCWSTR wzSddlFormat,
772 ...
773 )
774{
775 Assert(ppsd);
776 HRESULT hr = S_OK;
777 LPWSTR pwzSddl = NULL;
778 va_list args;
779 PSECURITY_DESCRIPTOR psd = NULL;
780 DWORD cbSD = 0;
781
782 *ppsd = NULL;
783
784 va_start(args, wzSddlFormat);
785 hr = StrAllocFormattedArgs(&pwzSddl, wzSddlFormat, args);
786 va_end(args);
787 AclExitOnFailure(hr, "failed to create SDDL string for format: %ls", wzSddlFormat);
788
789 if (!::ConvertStringSecurityDescriptorToSecurityDescriptorW(pwzSddl, SDDL_REVISION_1, &psd, &cbSD))
790 {
791 AclExitWithLastError(hr, "failed to create security descriptor from SDDL: %ls", pwzSddl);
792 }
793
794 *ppsd = static_cast<SECURITY_DESCRIPTOR*>(MemAlloc(cbSD, FALSE));
795 AclExitOnNull(*ppsd, hr, E_OUTOFMEMORY, "failed to allocate memory for security descriptor");
796
797 memcpy(*ppsd, psd, cbSD);
798 Assert(::IsValidSecurityDescriptor(*ppsd));
799
800 Assert(S_OK == hr);
801
802LExit:
803 if (FAILED(hr) && NULL != ppsd && NULL != *ppsd)
804 {
805 MemFree(*ppsd);
806 *ppsd = NULL;
807 }
808
809 if (psd)
810 {
811 ::LocalFree(psd);
812 }
813
814 ReleaseStr(pwzSddl);
815 return hr;
816}
817
818
819/********************************************************************
820AclDuplicateSecurityDescriptor - creates a copy of a self-relative security descriptor
821
822NOTE: passed in security descriptor must be in self-relative format
823********************************************************************/
824extern "C" HRESULT DAPI AclDuplicateSecurityDescriptor(
825 __in SECURITY_DESCRIPTOR* psd,
826 __deref_out SECURITY_DESCRIPTOR** ppsd
827 )
828{
829 HRESULT hr = S_OK;
830 DWORD cbSD;
831
832 AclExitOnNull(ppsd, hr, E_INVALIDARG, "Failed to get duplicate ACL security descriptor because no place to output was provided");
833 *ppsd = NULL;
834
835 //
836 // create the self-relative security descriptor
837 //
838 cbSD = ::GetSecurityDescriptorLength(psd);
839 *ppsd = static_cast<SECURITY_DESCRIPTOR*>(MemAlloc(cbSD, 0));
840 AclExitOnNull(*ppsd, hr, E_OUTOFMEMORY, "failed allocate memory for security descriptor");
841
842 memcpy(*ppsd, psd, cbSD);
843 Assert(::IsValidSecurityDescriptor(*ppsd));
844
845LExit:
846 if (FAILED(hr) && NULL != ppsd && NULL != *ppsd)
847 {
848 MemFree(*ppsd);
849 *ppsd = NULL;
850 }
851
852 return hr;
853}
854
855
856/********************************************************************
857AclGetSecurityDescriptor - returns self-relative security descriptor for named object
858
859NOTE: free ppsd with AclFreeSecurityDescriptor()
860********************************************************************/
861extern "C" HRESULT DAPI AclGetSecurityDescriptor(
862 __in_z LPCWSTR wzObject,
863 __in SE_OBJECT_TYPE sot,
864 __in SECURITY_INFORMATION securityInformation,
865 __deref_out SECURITY_DESCRIPTOR** ppsd
866 )
867{
868 HRESULT hr = S_OK;
869 DWORD er;
870 PSECURITY_DESCRIPTOR psd = NULL;
871 DWORD cbSD;
872
873 AclExitOnNull(ppsd, hr, E_INVALIDARG, "Failed to get ACL Security Descriptor because no place to output was provided");
874 *ppsd = NULL;
875
876 // get the security descriptor for the object
877 er = ::GetNamedSecurityInfoW(const_cast<LPWSTR>(wzObject), sot, securityInformation, NULL, NULL, NULL, NULL, &psd);
878 AclExitOnWin32Error(er, hr, "failed to get security info from object: %ls", wzObject);
879 Assert(::IsValidSecurityDescriptor(psd));
880
881 // copy the self-relative security descriptor
882 cbSD = ::GetSecurityDescriptorLength(psd);
883 *ppsd = static_cast<SECURITY_DESCRIPTOR*>(MemAlloc(cbSD, 0));
884 AclExitOnNull(*ppsd, hr, E_OUTOFMEMORY, "failed allocate memory for security descriptor");
885
886 memcpy(*ppsd, psd, cbSD);
887 Assert(::IsValidSecurityDescriptor(*ppsd));
888
889LExit:
890 if (FAILED(hr) && NULL != ppsd && NULL != *ppsd)
891 {
892 MemFree(*ppsd);
893 *ppsd = NULL;
894 }
895
896 if (psd)
897 {
898 ::LocalFree(psd);
899 }
900
901 return hr;
902}
903
904
905extern "C" HRESULT DAPI AclSetSecurityWithRetry(
906 __in_z LPCWSTR wzObject,
907 __in SE_OBJECT_TYPE sot,
908 __in SECURITY_INFORMATION securityInformation,
909 __in_opt PSID psidOwner,
910 __in_opt PSID psidGroup,
911 __in_opt PACL pDacl,
912 __in_opt PACL pSacl,
913 __in DWORD cRetry,
914 __in DWORD dwWaitMilliseconds
915 )
916{
917 HRESULT hr = S_OK;
918 LPWSTR sczObject = NULL;
919 DWORD i = 0;
920
921 hr = StrAllocString(&sczObject, wzObject, 0);
922 AclExitOnFailure(hr, "Failed to copy object to secure.");
923
924 hr = E_FAIL;
925 for (i = 0; FAILED(hr) && i <= cRetry; ++i)
926 {
927 if (0 < i)
928 {
929 ::Sleep(dwWaitMilliseconds);
930 }
931
932 DWORD er = ::SetNamedSecurityInfoW(sczObject, sot, securityInformation, psidOwner, psidGroup, pDacl, pSacl);
933 hr = HRESULT_FROM_WIN32(er);
934 }
935 AclExitOnRootFailure(hr, "Failed to set security on object '%ls' after %u retries.", wzObject, i);
936
937LExit:
938 ReleaseStr(sczObject);
939
940 return hr;
941}
942
943
944/********************************************************************
945AclFreeSid - frees a SID created by any Acl* functions
946
947********************************************************************/
948extern "C" HRESULT DAPI AclFreeSid(
949 __in PSID psid
950 )
951{
952 Assert(psid && ::IsValidSid(psid));
953 HRESULT hr = S_OK;
954
955 hr = MemFree(psid);
956
957 return hr;
958}
959
960
961/********************************************************************
962AclFreeDacl - frees a DACL created by any Acl* functions
963
964********************************************************************/
965extern "C" HRESULT DAPI AclFreeDacl(
966 __in ACL* pACL
967 )
968{
969 Assert(pACL);
970 HRESULT hr = S_OK;
971
972 hr = MemFree(pACL);
973
974 return hr;
975}
976
977
978/********************************************************************
979AclFreeSecurityDescriptor - frees a security descriptor created by any Acl* functions
980
981********************************************************************/
982extern "C" HRESULT DAPI AclFreeSecurityDescriptor(
983 __in SECURITY_DESCRIPTOR* psd
984 )
985{
986 Assert(psd && ::IsValidSecurityDescriptor(psd));
987 HRESULT hr = S_OK;
988
989 hr = MemFree(psd);
990
991 return hr;
992}
993
994
995/********************************************************************
996AclAddAdminToSecurityDescriptor - Adds the Administrators group to a security descriptor
997
998********************************************************************/
999extern "C" HRESULT DAPI AclAddAdminToSecurityDescriptor(
1000 __in SECURITY_DESCRIPTOR* pSecurity,
1001 __deref_out SECURITY_DESCRIPTOR** ppSecurityNew
1002 )
1003{
1004 HRESULT hr = S_OK;
1005 PACL pAcl = NULL;
1006 PACL pAclNew = NULL;
1007 BOOL fValid, fDaclDefaulted;
1008 ACL_ACE ace[1];
1009 SECURITY_DESCRIPTOR* pSecurityNew;
1010
1011 if (!::GetSecurityDescriptorDacl(pSecurity, &fValid, &pAcl, &fDaclDefaulted) || !fValid)
1012 {
1013 AclExitOnLastError(hr, "Failed to get acl from security descriptor");
1014 }
1015
1016 hr = AclGetWellKnownSid(WinBuiltinAdministratorsSid, &ace[0].psid);
1017 AclExitOnFailure(hr, "failed to get sid for Administrators group");
1018
1019 ace[0].dwFlags = NO_PROPAGATE_INHERIT_ACE;
1020 ace[0].dwMask = GENERIC_ALL;
1021
1022 hr = AclAddToDacl(pAcl, NULL, 0, ace, 1, &pAclNew);
1023 AclExitOnFailure(hr, "failed to add Administrators ACE to ACL");
1024
1025 hr = AclCreateSecurityDescriptorFromDacl(pAclNew, &pSecurityNew);
1026 AclExitOnLastError(hr, "Failed to create new security descriptor");
1027
1028 // The DACL is referenced by, not copied into, the security descriptor. Make sure not to free it.
1029 pAclNew = NULL;
1030
1031 *ppSecurityNew = pSecurityNew;
1032
1033LExit:
1034 if (pAclNew)
1035 {
1036 AclFreeDacl(pAclNew);
1037 }
1038 if (ace[0].psid)
1039 {
1040 AclFreeSid(ace[0].psid);
1041 }
1042
1043 return hr;
1044}
diff --git a/src/libs/dutil/WixToolset.DUtil/apputil.cpp b/src/libs/dutil/WixToolset.DUtil/apputil.cpp
new file mode 100644
index 00000000..589a09dd
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/apputil.cpp
@@ -0,0 +1,124 @@
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// Exit macros
6#define AppExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_APPUTIL, x, s, __VA_ARGS__)
7#define AppExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_APPUTIL, x, s, __VA_ARGS__)
8#define AppExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_APPUTIL, x, s, __VA_ARGS__)
9#define AppExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_APPUTIL, x, s, __VA_ARGS__)
10#define AppExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_APPUTIL, x, s, __VA_ARGS__)
11#define AppExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_APPUTIL, x, s, __VA_ARGS__)
12#define AppExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_APPUTIL, p, x, e, s, __VA_ARGS__)
13#define AppExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_APPUTIL, p, x, s, __VA_ARGS__)
14#define AppExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_APPUTIL, p, x, e, s, __VA_ARGS__)
15#define AppExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_APPUTIL, p, x, s, __VA_ARGS__)
16#define AppExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_APPUTIL, e, x, s, __VA_ARGS__)
17#define AppExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_APPUTIL, g, x, s, __VA_ARGS__)
18
19const DWORD PRIVATE_LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800;
20typedef BOOL(WINAPI *LPFN_SETDEFAULTDLLDIRECTORIES)(DWORD);
21typedef BOOL(WINAPI *LPFN_SETDLLDIRECTORYW)(LPCWSTR);
22
23extern "C" void DAPI AppFreeCommandLineArgs(
24 __in LPWSTR* argv
25 )
26{
27 // The "ignored" hack in AppParseCommandLine requires an adjustment.
28 LPWSTR* argvOriginal = argv - 1;
29 ::LocalFree(argvOriginal);
30}
31
32/********************************************************************
33AppInitialize - initializes the standard safety precautions for an
34 installation application.
35
36********************************************************************/
37extern "C" void DAPI AppInitialize(
38 __in_ecount(cSafelyLoadSystemDlls) LPCWSTR rgsczSafelyLoadSystemDlls[],
39 __in DWORD cSafelyLoadSystemDlls
40 )
41{
42 HRESULT hr = S_OK;
43 HMODULE hIgnored = NULL;
44 BOOL fSetDefaultDllDirectories = FALSE;
45
46 ::HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
47
48 // Best effort call to initialize default DLL directories to system only.
49 HMODULE hKernel32 = ::GetModuleHandleW(L"kernel32");
50 Assert(hKernel32);
51 LPFN_SETDEFAULTDLLDIRECTORIES pfnSetDefaultDllDirectories = (LPFN_SETDEFAULTDLLDIRECTORIES)::GetProcAddress(hKernel32, "SetDefaultDllDirectories");
52 if (pfnSetDefaultDllDirectories)
53 {
54 if (pfnSetDefaultDllDirectories(PRIVATE_LOAD_LIBRARY_SEARCH_SYSTEM32))
55 {
56 fSetDefaultDllDirectories = TRUE;
57 }
58 else
59 {
60 hr = HRESULT_FROM_WIN32(::GetLastError());
61 TraceError(hr, "Failed to call SetDefaultDllDirectories.");
62 }
63 }
64
65 // Only need to safely load if the default DLL directories was not
66 // able to be set.
67 if (!fSetDefaultDllDirectories)
68 {
69 // Remove current working directory from search order.
70 LPFN_SETDLLDIRECTORYW pfnSetDllDirectory = (LPFN_SETDLLDIRECTORYW)::GetProcAddress(hKernel32, "SetDllDirectoryW");
71 if (!pfnSetDllDirectory || !pfnSetDllDirectory(L""))
72 {
73 hr = HRESULT_FROM_WIN32(::GetLastError());
74 TraceError(hr, "Failed to call SetDllDirectory.");
75 }
76
77 for (DWORD i = 0; i < cSafelyLoadSystemDlls; ++i)
78 {
79 hr = LoadSystemLibrary(rgsczSafelyLoadSystemDlls[i], &hIgnored);
80 if (FAILED(hr))
81 {
82 TraceError(hr, "Failed to safety load: %ls", rgsczSafelyLoadSystemDlls[i]);
83 }
84 }
85 }
86}
87
88extern "C" void DAPI AppInitializeUnsafe()
89{
90 ::HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
91}
92
93extern "C" DAPI_(HRESULT) AppParseCommandLine(
94 __in LPCWSTR wzCommandLine,
95 __in int* pArgc,
96 __in LPWSTR** pArgv
97 )
98{
99 HRESULT hr = S_OK;
100 LPWSTR sczCommandLine = NULL;
101 LPWSTR* argv = NULL;
102 int argc = 0;
103
104 // CommandLineToArgvW tries to treat the first argument as the path to the process,
105 // which fails pretty miserably if your first argument is something like
106 // FOO="C:\Program Files\My Company". So give it something harmless to play with.
107 hr = StrAllocConcat(&sczCommandLine, L"ignored ", 0);
108 AppExitOnFailure(hr, "Failed to initialize command line.");
109
110 hr = StrAllocConcat(&sczCommandLine, wzCommandLine, 0);
111 AppExitOnFailure(hr, "Failed to copy command line.");
112
113 argv = ::CommandLineToArgvW(sczCommandLine, &argc);
114 AppExitOnNullWithLastError(argv, hr, "Failed to parse command line.");
115
116 // Skip "ignored" argument/hack.
117 *pArgv = argv + 1;
118 *pArgc = argc - 1;
119
120LExit:
121 ReleaseStr(sczCommandLine);
122
123 return hr;
124}
diff --git a/src/libs/dutil/WixToolset.DUtil/apuputil.cpp b/src/libs/dutil/WixToolset.DUtil/apuputil.cpp
new file mode 100644
index 00000000..eb96d515
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/apuputil.cpp
@@ -0,0 +1,700 @@
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// Exit macros
6#define ApupExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_APUPUTIL, x, s, __VA_ARGS__)
7#define ApupExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_APUPUTIL, x, s, __VA_ARGS__)
8#define ApupExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_APUPUTIL, x, s, __VA_ARGS__)
9#define ApupExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_APUPUTIL, x, s, __VA_ARGS__)
10#define ApupExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_APUPUTIL, x, s, __VA_ARGS__)
11#define ApupExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_APUPUTIL, x, s, __VA_ARGS__)
12#define ApupExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_APUPUTIL, p, x, e, s, __VA_ARGS__)
13#define ApupExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_APUPUTIL, p, x, s, __VA_ARGS__)
14#define ApupExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_APUPUTIL, p, x, e, s, __VA_ARGS__)
15#define ApupExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_APUPUTIL, p, x, s, __VA_ARGS__)
16#define ApupExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_APUPUTIL, e, x, s, __VA_ARGS__)
17#define ApupExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_APUPUTIL, g, x, s, __VA_ARGS__)
18
19// prototypes
20static HRESULT ProcessEntry(
21 __in ATOM_ENTRY* pAtomEntry,
22 __in LPCWSTR wzDefaultAppId,
23 __inout APPLICATION_UPDATE_ENTRY* pApupEntry
24 );
25static HRESULT ParseEnclosure(
26 __in ATOM_LINK* pLink,
27 __in APPLICATION_UPDATE_ENCLOSURE* pEnclosure
28 );
29static __callback int __cdecl CompareEntries(
30 void* pvContext,
31 const void* pvLeft,
32 const void* pvRight
33 );
34static HRESULT FilterEntries(
35 __in APPLICATION_UPDATE_ENTRY* rgEntries,
36 __in DWORD cEntries,
37 __in VERUTIL_VERSION* pCurrentVersion,
38 __inout APPLICATION_UPDATE_ENTRY** prgFilteredEntries,
39 __inout DWORD* pcFilteredEntries
40 );
41static HRESULT CopyEntry(
42 __in const APPLICATION_UPDATE_ENTRY* pSrc,
43 __in APPLICATION_UPDATE_ENTRY* pDest
44 );
45static HRESULT CopyEnclosure(
46 __in const APPLICATION_UPDATE_ENCLOSURE* pSrc,
47 __in APPLICATION_UPDATE_ENCLOSURE* pDest
48 );
49static void FreeEntry(
50 __in APPLICATION_UPDATE_ENTRY* pApupEntry
51 );
52static void FreeEnclosure(
53 __in APPLICATION_UPDATE_ENCLOSURE* pEnclosure
54 );
55
56
57//
58// ApupCalculateChainFromAtom - returns the chain of application updates found in an ATOM feed.
59//
60extern "C" HRESULT DAPI ApupAllocChainFromAtom(
61 __in ATOM_FEED* pFeed,
62 __out APPLICATION_UPDATE_CHAIN** ppChain
63 )
64{
65 HRESULT hr = S_OK;
66 APPLICATION_UPDATE_CHAIN* pChain = NULL;
67
68 pChain = static_cast<APPLICATION_UPDATE_CHAIN*>(MemAlloc(sizeof(APPLICATION_UPDATE_CHAIN), TRUE));
69
70 // First search the ATOM feed's custom elements to try and find the default application identity.
71 for (ATOM_UNKNOWN_ELEMENT* pElement = pFeed->pUnknownElements; pElement; pElement = pElement->pNext)
72 {
73 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzNamespace, -1, APPLICATION_SYNDICATION_NAMESPACE, -1))
74 {
75 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzElement, -1, L"application", -1))
76 {
77 hr = StrAllocString(&pChain->wzDefaultApplicationId, pElement->wzValue, 0);
78 ApupExitOnFailure(hr, "Failed to allocate default application id.");
79
80 for (ATOM_UNKNOWN_ATTRIBUTE* pAttribute = pElement->pAttributes; pAttribute; pAttribute = pAttribute->pNext)
81 {
82 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pAttribute->wzAttribute, -1, L"type", -1))
83 {
84 hr = StrAllocString(&pChain->wzDefaultApplicationType, pAttribute->wzValue, 0);
85 ApupExitOnFailure(hr, "Failed to allocate default application type.");
86 }
87 }
88 }
89 }
90 }
91
92 // Assume there will be as many application updates entries as their are feed entries.
93 if (pFeed->cEntries)
94 {
95 pChain->rgEntries = static_cast<APPLICATION_UPDATE_ENTRY*>(MemAlloc(sizeof(APPLICATION_UPDATE_ENTRY) * pFeed->cEntries, TRUE));
96 ApupExitOnNull(pChain->rgEntries, hr, E_OUTOFMEMORY, "Failed to allocate memory for update entries.");
97
98 // Process each entry, building up the chain.
99 for (DWORD i = 0; i < pFeed->cEntries; ++i)
100 {
101 hr = ProcessEntry(pFeed->rgEntries + i, pChain->wzDefaultApplicationId, pChain->rgEntries + pChain->cEntries);
102 ApupExitOnFailure(hr, "Failed to process ATOM entry.");
103
104 if (S_FALSE != hr)
105 {
106 ++pChain->cEntries;
107 }
108 }
109
110 // Sort the chain by descending version and ascending total size.
111 qsort_s(pChain->rgEntries, pChain->cEntries, sizeof(APPLICATION_UPDATE_ENTRY), CompareEntries, NULL);
112 }
113
114 // Trim the unused entries from the end, if any of the entries failed to parse or validate
115 if (pChain->cEntries != pFeed->cEntries)
116 {
117 if (pChain->cEntries > 0)
118 {
119 pChain->rgEntries = static_cast<APPLICATION_UPDATE_ENTRY*>(MemReAlloc(pChain->rgEntries, sizeof(APPLICATION_UPDATE_ENTRY) * pChain->cEntries, FALSE));
120 ApupExitOnNull(pChain->rgEntries, hr, E_OUTOFMEMORY, "Failed to reallocate memory for update entries.");
121 }
122 else
123 {
124 ReleaseNullMem(pChain->rgEntries);
125 }
126 }
127
128 *ppChain = pChain;
129 pChain = NULL;
130
131LExit:
132 ReleaseApupChain(pChain);
133
134 return hr;
135}
136
137
138//
139// ApupFilterChain - remove the unneeded update elements from the chain.
140//
141HRESULT DAPI ApupFilterChain(
142 __in APPLICATION_UPDATE_CHAIN* pChain,
143 __in VERUTIL_VERSION* pVersion,
144 __out APPLICATION_UPDATE_CHAIN** ppFilteredChain
145 )
146{
147 HRESULT hr = S_OK;
148 APPLICATION_UPDATE_CHAIN* pNewChain = NULL;
149 APPLICATION_UPDATE_ENTRY* prgEntries = NULL;
150 DWORD cEntries = NULL;
151
152 pNewChain = static_cast<APPLICATION_UPDATE_CHAIN*>(MemAlloc(sizeof(APPLICATION_UPDATE_CHAIN), TRUE));
153 ApupExitOnNull(pNewChain, hr, E_OUTOFMEMORY, "Failed to allocate filtered chain.");
154
155 hr = FilterEntries(pChain->rgEntries, pChain->cEntries, pVersion, &prgEntries, &cEntries);
156 ApupExitOnFailure(hr, "Failed to filter entries by version.");
157
158 if (pChain->wzDefaultApplicationId)
159 {
160 hr = StrAllocString(&pNewChain->wzDefaultApplicationId, pChain->wzDefaultApplicationId, 0);
161 ApupExitOnFailure(hr, "Failed to copy default application id.");
162 }
163
164 if (pChain->wzDefaultApplicationType)
165 {
166 hr = StrAllocString(&pNewChain->wzDefaultApplicationType, pChain->wzDefaultApplicationType, 0);
167 ApupExitOnFailure(hr, "Failed to copy default application type.");
168 }
169
170 pNewChain->rgEntries = prgEntries;
171 pNewChain->cEntries = cEntries;
172
173 *ppFilteredChain = pNewChain;
174 pNewChain = NULL;
175
176LExit:
177 ReleaseApupChain(pNewChain);
178 return hr;
179}
180
181
182//
183// ApupFreeChain - frees a previously allocated application update chain.
184//
185extern "C" void DAPI ApupFreeChain(
186 __in APPLICATION_UPDATE_CHAIN* pChain
187 )
188{
189 if (pChain)
190 {
191 for (DWORD i = 0; i < pChain->cEntries; ++i)
192 {
193 FreeEntry(pChain->rgEntries + i);
194 }
195
196 ReleaseMem(pChain->rgEntries);
197 ReleaseStr(pChain->wzDefaultApplicationType);
198 ReleaseStr(pChain->wzDefaultApplicationId);
199 ReleaseMem(pChain);
200 }
201}
202
203
204static HRESULT ProcessEntry(
205 __in ATOM_ENTRY* pAtomEntry,
206 __in LPCWSTR wzDefaultAppId,
207 __inout APPLICATION_UPDATE_ENTRY* pApupEntry
208 )
209{
210 HRESULT hr = S_OK;
211 int nCompareResult = 0;
212
213 // First search the ATOM entry's custom elements to try and find the application update information.
214 for (ATOM_UNKNOWN_ELEMENT* pElement = pAtomEntry->pUnknownElements; pElement; pElement = pElement->pNext)
215 {
216 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzNamespace, -1, APPLICATION_SYNDICATION_NAMESPACE, -1))
217 {
218 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzElement, -1, L"application", -1))
219 {
220 hr = StrAllocString(&pApupEntry->wzApplicationId, pElement->wzValue, 0);
221 ApupExitOnFailure(hr, "Failed to allocate application identity.");
222
223 for (ATOM_UNKNOWN_ATTRIBUTE* pAttribute = pElement->pAttributes; pAttribute; pAttribute = pAttribute->pNext)
224 {
225 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pAttribute->wzAttribute, -1, L"type", -1))
226 {
227 hr = StrAllocString(&pApupEntry->wzApplicationType, pAttribute->wzValue, 0);
228 ApupExitOnFailure(hr, "Failed to allocate application type.");
229 }
230 }
231 }
232 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzElement, -1, L"upgrade", -1))
233 {
234 hr = StrAllocString(&pApupEntry->wzUpgradeId, pElement->wzValue, 0);
235 ApupExitOnFailure(hr, "Failed to allocate upgrade id.");
236
237 for (ATOM_UNKNOWN_ATTRIBUTE* pAttribute = pElement->pAttributes; pAttribute; pAttribute = pAttribute->pNext)
238 {
239 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pAttribute->wzAttribute, -1, L"version", -1))
240 {
241 hr = VerParseVersion(pAttribute->wzValue, 0, FALSE, &pApupEntry->pUpgradeVersion);
242 ApupExitOnFailure(hr, "Failed to parse upgrade version string '%ls' from ATOM entry.", pAttribute->wzValue);
243 }
244 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pAttribute->wzAttribute, -1, L"exclusive", -1))
245 {
246 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pAttribute->wzValue, -1, L"true", -1))
247 {
248 pApupEntry->fUpgradeExclusive = TRUE;
249 }
250 }
251 }
252 }
253 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzElement, -1, L"version", -1))
254 {
255 hr = VerParseVersion(pElement->wzValue, 0, FALSE, &pApupEntry->pVersion);
256 ApupExitOnFailure(hr, "Failed to parse version string '%ls' from ATOM entry.", pElement->wzValue);
257 }
258 }
259 }
260
261 // If there is no application identity or no version, skip the whole thing.
262 if ((!pApupEntry->wzApplicationId && !wzDefaultAppId) || !pApupEntry->pVersion)
263 {
264 ExitFunction1(hr = S_FALSE); // skip this update since it has no application id or version.
265 }
266
267 if (pApupEntry->pUpgradeVersion)
268 {
269 hr = VerCompareParsedVersions(pApupEntry->pUpgradeVersion, pApupEntry->pVersion, &nCompareResult);
270 ApupExitOnFailure(hr, "Failed to compare version to upgrade version.");
271
272 if (nCompareResult >= 0)
273 {
274 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
275 ApupExitOnRootFailure(hr, "Upgrade version is greater than or equal to application version.");
276 }
277 }
278
279 if (pAtomEntry->wzTitle)
280 {
281 hr = StrAllocString(&pApupEntry->wzTitle, pAtomEntry->wzTitle, 0);
282 ApupExitOnFailure(hr, "Failed to allocate application title.");
283 }
284
285 if (pAtomEntry->wzSummary)
286 {
287 hr = StrAllocString(&pApupEntry->wzSummary, pAtomEntry->wzSummary, 0);
288 ApupExitOnFailure(hr, "Failed to allocate application summary.");
289 }
290
291 if (pAtomEntry->pContent)
292 {
293 if (pAtomEntry->pContent->wzType)
294 {
295 hr = StrAllocString(&pApupEntry->wzContentType, pAtomEntry->pContent->wzType, 0);
296 ApupExitOnFailure(hr, "Failed to allocate content type.");
297 }
298
299 if (pAtomEntry->pContent->wzValue)
300 {
301 hr = StrAllocString(&pApupEntry->wzContent, pAtomEntry->pContent->wzValue, 0);
302 ApupExitOnFailure(hr, "Failed to allocate content.");
303 }
304 }
305 // Now process the enclosures. Assume every link in the ATOM entry is an enclosure.
306 pApupEntry->rgEnclosures = static_cast<APPLICATION_UPDATE_ENCLOSURE*>(MemAlloc(sizeof(APPLICATION_UPDATE_ENCLOSURE) * pAtomEntry->cLinks, TRUE));
307 ApupExitOnNull(pApupEntry->rgEnclosures, hr, E_OUTOFMEMORY, "Failed to allocate enclosures for application update entry.");
308
309 for (DWORD i = 0; i < pAtomEntry->cLinks; ++i)
310 {
311 ATOM_LINK* pLink = pAtomEntry->rgLinks + i;
312 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pLink->wzRel, -1, L"enclosure", -1))
313 {
314 hr = ParseEnclosure(pLink, pApupEntry->rgEnclosures + pApupEntry->cEnclosures);
315 ApupExitOnFailure(hr, "Failed to parse enclosure.");
316
317 pApupEntry->dw64TotalSize += pApupEntry->rgEnclosures[pApupEntry->cEnclosures].dw64Size; // total up the size of the enclosures
318
319 ++pApupEntry->cEnclosures;
320 }
321 }
322
323LExit:
324 if (S_OK != hr) // if anything went wrong, free the entry.
325 {
326 FreeEntry(pApupEntry);
327 memset(pApupEntry, 0, sizeof(APPLICATION_UPDATE_ENTRY));
328 }
329
330 return hr;
331}
332
333
334static HRESULT ParseEnclosure(
335 __in ATOM_LINK* pLink,
336 __in APPLICATION_UPDATE_ENCLOSURE* pEnclosure
337 )
338{
339 HRESULT hr = S_OK;
340 DWORD dwDigestLength = 0;
341 DWORD dwDigestStringLength = 0;
342 size_t cchDigestString = 0;
343
344 // First search the ATOM link's custom elements to try and find the application update enclosure information.
345 for (ATOM_UNKNOWN_ELEMENT* pElement = pLink->pUnknownElements; pElement; pElement = pElement->pNext)
346 {
347 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pElement->wzNamespace, -1, APPLICATION_SYNDICATION_NAMESPACE, -1))
348 {
349 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, L"digest", -1, pElement->wzElement, -1))
350 {
351 // Find the digest[@algorithm] which is required. Everything else is ignored.
352 for (ATOM_UNKNOWN_ATTRIBUTE* pAttribute = pElement->pAttributes; pAttribute; pAttribute = pAttribute->pNext)
353 {
354 dwDigestLength = 0;
355 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, L"algorithm", -1, pAttribute->wzAttribute, -1))
356 {
357 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, L"md5", -1, pAttribute->wzValue, -1))
358 {
359 pEnclosure->digestAlgorithm = APUP_HASH_ALGORITHM_MD5;
360 dwDigestLength = MD5_HASH_LEN;
361 }
362 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, L"sha1", -1, pAttribute->wzValue, -1))
363 {
364 pEnclosure->digestAlgorithm = APUP_HASH_ALGORITHM_SHA1;
365 dwDigestLength = SHA1_HASH_LEN;
366 }
367 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, L"sha256", -1, pAttribute->wzValue, -1))
368 {
369 pEnclosure->digestAlgorithm = APUP_HASH_ALGORITHM_SHA256;
370 dwDigestLength = SHA256_HASH_LEN;
371 }
372 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, L"sha512", -1, pAttribute->wzValue, -1))
373 {
374 pEnclosure->digestAlgorithm = APUP_HASH_ALGORITHM_SHA512;
375 dwDigestLength = SHA512_HASH_LEN;
376 }
377 break;
378 }
379 }
380
381 if (dwDigestLength)
382 {
383 dwDigestStringLength = 2 * dwDigestLength;
384
385 hr = ::StringCchLengthW(pElement->wzValue, STRSAFE_MAX_CCH, &cchDigestString);
386 ApupExitOnFailure(hr, "Failed to get string length of digest value.");
387
388 if (dwDigestStringLength != cchDigestString)
389 {
390 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
391 ApupExitOnRootFailure(hr, "Invalid digest length (%Iu) for digest algorithm (%u).", cchDigestString, dwDigestStringLength);
392 }
393
394 pEnclosure->cbDigest = sizeof(BYTE) * dwDigestLength;
395 pEnclosure->rgbDigest = static_cast<BYTE*>(MemAlloc(pEnclosure->cbDigest, TRUE));
396 ApupExitOnNull(pEnclosure->rgbDigest, hr, E_OUTOFMEMORY, "Failed to allocate memory for digest.");
397
398 hr = StrHexDecode(pElement->wzValue, pEnclosure->rgbDigest, pEnclosure->cbDigest);
399 ApupExitOnFailure(hr, "Failed to decode digest value.");
400 }
401 else
402 {
403 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
404 ApupExitOnRootFailure(hr, "Unknown algorithm type for digest.");
405 }
406
407 break;
408 }
409 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, L"name", -1, pElement->wzElement, -1))
410 {
411 hr = StrAllocString(&pEnclosure->wzLocalName, pElement->wzValue, 0);
412 ApupExitOnFailure(hr, "Failed to copy local name.");
413 }
414 }
415 }
416
417 pEnclosure->dw64Size = pLink->dw64Length;
418
419 hr = StrAllocString(&pEnclosure->wzUrl, pLink->wzUrl, 0);
420 ApupExitOnFailure(hr, "Failed to allocate enclosure URL.");
421
422 pEnclosure->fInstaller = FALSE;
423 pEnclosure->wzLocalName = NULL;
424
425LExit:
426 return hr;
427}
428
429
430static __callback int __cdecl CompareEntries(
431 void* /*pvContext*/,
432 const void* pvLeft,
433 const void* pvRight
434 )
435{
436 int ret = 0;
437 const APPLICATION_UPDATE_ENTRY* pEntryLeft = static_cast<const APPLICATION_UPDATE_ENTRY*>(pvLeft);
438 const APPLICATION_UPDATE_ENTRY* pEntryRight = static_cast<const APPLICATION_UPDATE_ENTRY*>(pvRight);
439
440 VerCompareParsedVersions(pEntryLeft->pVersion, pEntryRight->pVersion, &ret);
441 if (0 == ret)
442 {
443 VerCompareParsedVersions(pEntryLeft->pUpgradeVersion, pEntryRight->pUpgradeVersion, &ret);
444 if (0 == ret)
445 {
446 ret = (pEntryLeft->dw64TotalSize < pEntryRight->dw64TotalSize) ? -1 :
447 (pEntryLeft->dw64TotalSize > pEntryRight->dw64TotalSize) ? 1 : 0;
448 }
449 }
450
451 // Sort descending.
452 ret = -ret;
453
454 return ret;
455}
456
457
458static HRESULT FilterEntries(
459 __in APPLICATION_UPDATE_ENTRY* rgEntries,
460 __in DWORD cEntries,
461 __in VERUTIL_VERSION* pCurrentVersion,
462 __inout APPLICATION_UPDATE_ENTRY** prgFilteredEntries,
463 __inout DWORD* pcFilteredEntries
464 )
465{
466 HRESULT hr = S_OK;
467 int nCompareResult = 0;
468 size_t cbAllocSize = 0;
469 const APPLICATION_UPDATE_ENTRY* pRequired = NULL;;
470 LPVOID pv = NULL;
471
472 if (cEntries)
473 {
474 for (DWORD i = 0; i < cEntries; ++i)
475 {
476 const APPLICATION_UPDATE_ENTRY* pEntry = rgEntries + i;
477
478 hr = VerCompareParsedVersions(pCurrentVersion, pEntry->pVersion, &nCompareResult);
479 ApupExitOnFailure(hr, "Failed to compare versions.");
480
481 if (nCompareResult >= 0)
482 {
483 continue;
484 }
485
486 hr = VerCompareParsedVersions(pCurrentVersion, pEntry->pUpgradeVersion, &nCompareResult);
487 ApupExitOnFailure(hr, "Failed to compare upgrade versions.");
488
489 if (nCompareResult > 0 || (!pEntry->fUpgradeExclusive && nCompareResult == 0))
490 {
491 pRequired = pEntry;
492 break;
493 }
494 }
495
496 if (pRequired)
497 {
498 DWORD cNewFilteredEntries = *pcFilteredEntries + 1;
499
500 hr = ::SizeTMult(sizeof(APPLICATION_UPDATE_ENTRY), cNewFilteredEntries, &cbAllocSize);
501 ApupExitOnFailure(hr, "Overflow while calculating alloc size for more entries - number of entries: %u", cNewFilteredEntries);
502
503 if (*prgFilteredEntries)
504 {
505 pv = MemReAlloc(*prgFilteredEntries, cbAllocSize, FALSE);
506 ApupExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to reallocate memory for more entries.");
507 }
508 else
509 {
510 pv = MemAlloc(cbAllocSize, TRUE);
511 ApupExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for entries.");
512 }
513
514 *pcFilteredEntries = cNewFilteredEntries;
515 *prgFilteredEntries = static_cast<APPLICATION_UPDATE_ENTRY*>(pv);
516 pv = NULL;
517
518 hr = CopyEntry(pRequired, *prgFilteredEntries + *pcFilteredEntries - 1);
519 ApupExitOnFailure(hr, "Failed to deep copy entry.");
520
521 hr = VerCompareParsedVersions(pRequired->pVersion, rgEntries[0].pVersion, &nCompareResult);
522 ApupExitOnFailure(hr, "Failed to compare required version.");
523
524 if (nCompareResult < 0)
525 {
526 FilterEntries(rgEntries, cEntries, pRequired->pVersion, prgFilteredEntries, pcFilteredEntries);
527 }
528 }
529 }
530
531LExit:
532 ReleaseMem(pv);
533 return hr;
534}
535
536
537static HRESULT CopyEntry(
538 __in const APPLICATION_UPDATE_ENTRY* pSrc,
539 __in APPLICATION_UPDATE_ENTRY* pDest
540 )
541{
542 HRESULT hr = S_OK;
543 size_t cbAllocSize = 0;
544
545 memset(pDest, 0, sizeof(APPLICATION_UPDATE_ENTRY));
546
547 if (pSrc->wzApplicationId)
548 {
549 hr = StrAllocString(&pDest->wzApplicationId, pSrc->wzApplicationId, 0);
550 ApupExitOnFailure(hr, "Failed to copy application id.");
551 }
552
553 if (pSrc->wzApplicationType)
554 {
555 hr = StrAllocString(&pDest->wzApplicationType, pSrc->wzApplicationType, 0);
556 ApupExitOnFailure(hr, "Failed to copy application type.");
557 }
558
559 if (pSrc->wzUpgradeId)
560 {
561 hr = StrAllocString(&pDest->wzUpgradeId, pSrc->wzUpgradeId, 0);
562 ApupExitOnFailure(hr, "Failed to copy upgrade id.");
563 }
564
565 if (pSrc->wzTitle)
566 {
567 hr = StrAllocString(&pDest->wzTitle, pSrc->wzTitle, 0);
568 ApupExitOnFailure(hr, "Failed to copy title.");
569 }
570
571 if (pSrc->wzSummary)
572 {
573 hr = StrAllocString(&pDest->wzSummary, pSrc->wzSummary, 0);
574 ApupExitOnFailure(hr, "Failed to copy summary.");
575 }
576
577 if (pSrc->wzContentType)
578 {
579 hr = StrAllocString(&pDest->wzContentType, pSrc->wzContentType, 0);
580 ApupExitOnFailure(hr, "Failed to copy content type.");
581 }
582
583 if (pSrc->wzContent)
584 {
585 hr = StrAllocString(&pDest->wzContent, pSrc->wzContent, 0);
586 ApupExitOnFailure(hr, "Failed to copy content.");
587 }
588
589 pDest->dw64TotalSize = pSrc->dw64TotalSize;
590
591 hr = VerCopyVersion(pSrc->pUpgradeVersion, &pDest->pUpgradeVersion);
592 ApupExitOnFailure(hr, "Failed to copy upgrade version.");
593
594 hr = VerCopyVersion(pSrc->pVersion, &pDest->pVersion);
595 ApupExitOnFailure(hr, "Failed to copy version.");
596
597 pDest->fUpgradeExclusive = pSrc->fUpgradeExclusive;
598
599 hr = ::SizeTMult(sizeof(APPLICATION_UPDATE_ENCLOSURE), pSrc->cEnclosures, &cbAllocSize);
600 ApupExitOnRootFailure(hr, "Overflow while calculating memory allocation size");
601
602 pDest->rgEnclosures = static_cast<APPLICATION_UPDATE_ENCLOSURE*>(MemAlloc(cbAllocSize, TRUE));
603 ApupExitOnNull(pDest->rgEnclosures, hr, E_OUTOFMEMORY, "Failed to allocate copy of enclosures.");
604
605 pDest->cEnclosures = pSrc->cEnclosures;
606
607 for (DWORD i = 0; i < pDest->cEnclosures; ++i)
608 {
609 hr = CopyEnclosure(pSrc->rgEnclosures + i, pDest->rgEnclosures + i);
610 ApupExitOnFailure(hr, "Failed to copy enclosure.");
611 }
612
613LExit:
614 if (FAILED(hr))
615 {
616 FreeEntry(pDest);
617 }
618
619 return hr;
620}
621
622
623static HRESULT CopyEnclosure(
624 __in const APPLICATION_UPDATE_ENCLOSURE* pSrc,
625 __in APPLICATION_UPDATE_ENCLOSURE* pDest
626 )
627{
628 HRESULT hr = S_OK;
629
630 memset(pDest, 0, sizeof(APPLICATION_UPDATE_ENCLOSURE));
631
632 if (pSrc->wzUrl)
633 {
634 hr = StrAllocString(&pDest->wzUrl, pSrc->wzUrl, 0);
635 ApupExitOnFailure(hr, "Failed copy url.");
636 }
637
638 if (pSrc->wzLocalName)
639 {
640 hr = StrAllocString(&pDest->wzLocalName, pSrc->wzLocalName, 0);
641 ApupExitOnFailure(hr, "Failed copy url.");
642 }
643
644 pDest->rgbDigest = static_cast<BYTE*>(MemAlloc(sizeof(BYTE) * pSrc->cbDigest, FALSE));
645 ApupExitOnNull(pDest->rgbDigest, hr, E_OUTOFMEMORY, "Failed to allocate memory for copy of digest.");
646
647 pDest->cbDigest = pSrc->cbDigest;
648
649 memcpy_s(pDest->rgbDigest, sizeof(BYTE) * pDest->cbDigest, pSrc->rgbDigest, sizeof(BYTE) * pSrc->cbDigest);
650
651 pDest->digestAlgorithm = pSrc->digestAlgorithm;
652
653 pDest->dw64Size = pSrc->dw64Size;
654 pDest->fInstaller = pSrc->fInstaller;
655
656LExit:
657 if (FAILED(hr))
658 {
659 FreeEnclosure(pDest);
660 }
661
662 return hr;
663}
664
665
666static void FreeEntry(
667 __in APPLICATION_UPDATE_ENTRY* pEntry
668 )
669{
670 if (pEntry)
671 {
672 for (DWORD i = 0; i < pEntry->cEnclosures; ++i)
673 {
674 FreeEnclosure(pEntry->rgEnclosures + i);
675 }
676
677 ReleaseStr(pEntry->wzUpgradeId);
678 ReleaseStr(pEntry->wzApplicationType);
679 ReleaseStr(pEntry->wzApplicationId);
680 ReleaseStr(pEntry->wzTitle);
681 ReleaseStr(pEntry->wzSummary);
682 ReleaseStr(pEntry->wzContentType);
683 ReleaseStr(pEntry->wzContent);
684 ReleaseVerutilVersion(pEntry->pVersion);
685 ReleaseVerutilVersion(pEntry->pUpgradeVersion);
686 }
687}
688
689
690static void FreeEnclosure(
691 __in APPLICATION_UPDATE_ENCLOSURE* pEnclosure
692 )
693{
694 if (pEnclosure)
695 {
696 ReleaseMem(pEnclosure->rgbDigest);
697 ReleaseStr(pEnclosure->wzLocalName);
698 ReleaseStr(pEnclosure->wzUrl);
699 }
700}
diff --git a/src/libs/dutil/WixToolset.DUtil/atomutil.cpp b/src/libs/dutil/WixToolset.DUtil/atomutil.cpp
new file mode 100644
index 00000000..c7c7975a
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/atomutil.cpp
@@ -0,0 +1,1297 @@
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// Exit macros
6#define AtomExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_ATOMUTIL, x, s, __VA_ARGS__)
7#define AtomExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_ATOMUTIL, x, s, __VA_ARGS__)
8#define AtomExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_ATOMUTIL, x, s, __VA_ARGS__)
9#define AtomExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_ATOMUTIL, x, s, __VA_ARGS__)
10#define AtomExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_ATOMUTIL, x, s, __VA_ARGS__)
11#define AtomExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_ATOMUTIL, x, s, __VA_ARGS__)
12#define AtomExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_ATOMUTIL, p, x, e, s, __VA_ARGS__)
13#define AtomExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_ATOMUTIL, p, x, s, __VA_ARGS__)
14#define AtomExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_ATOMUTIL, p, x, e, s, __VA_ARGS__)
15#define AtomExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_ATOMUTIL, p, x, s, __VA_ARGS__)
16#define AtomExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_ATOMUTIL, e, x, s, __VA_ARGS__)
17#define AtomExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_ATOMUTIL, g, x, s, __VA_ARGS__)
18
19static HRESULT ParseAtomDocument(
20 __in IXMLDOMDocument *pixd,
21 __out ATOM_FEED **ppFeed
22 );
23static HRESULT ParseAtomFeed(
24 __in IXMLDOMNode *pixnFeed,
25 __out ATOM_FEED **ppFeed
26 );
27static HRESULT ParseAtomAuthor(
28 __in IXMLDOMNode* pixnAuthor,
29 __in ATOM_AUTHOR* pAuthor
30 );
31static HRESULT ParseAtomCategory(
32 __in IXMLDOMNode* pixnCategory,
33 __in ATOM_CATEGORY* pCategory
34 );
35static HRESULT ParseAtomEntry(
36 __in IXMLDOMNode* pixnEntry,
37 __in ATOM_ENTRY* pEntry
38 );
39static HRESULT ParseAtomLink(
40 __in IXMLDOMNode* pixnLink,
41 __in ATOM_LINK* pLink
42 );
43static HRESULT ParseAtomUnknownElement(
44 __in IXMLDOMNode *pNode,
45 __inout ATOM_UNKNOWN_ELEMENT** ppUnknownElement
46 );
47static HRESULT ParseAtomUnknownAttribute(
48 __in IXMLDOMNode *pNode,
49 __inout ATOM_UNKNOWN_ATTRIBUTE** ppUnknownAttribute
50 );
51static HRESULT AssignDateTime(
52 __in FILETIME* pft,
53 __in IXMLDOMNode* pNode
54 );
55static HRESULT AssignString(
56 __out_z LPWSTR* pwzValue,
57 __in IXMLDOMNode* pNode
58 );
59static void FreeAtomAuthor(
60 __in_opt ATOM_AUTHOR* pAuthor
61 );
62static void FreeAtomContent(
63 __in_opt ATOM_CONTENT* pContent
64 );
65static void FreeAtomCategory(
66 __in_opt ATOM_CATEGORY* pCategory
67 );
68static void FreeAtomEntry(
69 __in_opt ATOM_ENTRY* pEntry
70 );
71static void FreeAtomLink(
72 __in_opt ATOM_LINK* pLink
73 );
74static void FreeAtomUnknownElementList(
75 __in_opt ATOM_UNKNOWN_ELEMENT* pUnknownElement
76 );
77static void FreeAtomUnknownAttributeList(
78 __in_opt ATOM_UNKNOWN_ATTRIBUTE* pUnknownAttribute
79 );
80
81template<class T> static HRESULT AllocateAtomType(
82 __in IXMLDOMNode* pixnParent,
83 __in LPCWSTR wzT,
84 __out T** pprgT,
85 __out DWORD* pcT
86 );
87
88
89/********************************************************************
90 AtomInitialize - Initialize ATOM utilities.
91
92*********************************************************************/
93extern "C" HRESULT DAPI AtomInitialize()
94{
95 return XmlInitialize();
96}
97
98
99/********************************************************************
100 AtomUninitialize - Uninitialize ATOM utilities.
101
102*********************************************************************/
103extern "C" void DAPI AtomUninitialize()
104{
105 XmlUninitialize();
106}
107
108
109/********************************************************************
110 AtomParseFromString - parses out an ATOM feed from a string.
111
112*********************************************************************/
113extern "C" HRESULT DAPI AtomParseFromString(
114 __in_z LPCWSTR wzAtomString,
115 __out ATOM_FEED **ppFeed
116 )
117{
118 Assert(wzAtomString);
119 Assert(ppFeed);
120
121 HRESULT hr = S_OK;
122 ATOM_FEED *pNewFeed = NULL;
123 IXMLDOMDocument *pixdAtom = NULL;
124
125 hr = XmlLoadDocument(wzAtomString, &pixdAtom);
126 AtomExitOnFailure(hr, "Failed to load ATOM string as XML document.");
127
128 hr = ParseAtomDocument(pixdAtom, &pNewFeed);
129 AtomExitOnFailure(hr, "Failed to parse ATOM document.");
130
131 *ppFeed = pNewFeed;
132 pNewFeed = NULL;
133
134LExit:
135 ReleaseAtomFeed(pNewFeed);
136 ReleaseObject(pixdAtom);
137
138 return hr;
139}
140
141
142/********************************************************************
143 AtomParseFromFile - parses out an ATOM feed from a file path.
144
145*********************************************************************/
146extern "C" HRESULT DAPI AtomParseFromFile(
147 __in_z LPCWSTR wzAtomFile,
148 __out ATOM_FEED **ppFeed
149 )
150{
151 Assert(wzAtomFile);
152 Assert(ppFeed);
153
154 HRESULT hr = S_OK;
155 ATOM_FEED *pNewFeed = NULL;
156 IXMLDOMDocument *pixdAtom = NULL;
157
158 hr = XmlLoadDocumentFromFile(wzAtomFile, &pixdAtom);
159 AtomExitOnFailure(hr, "Failed to load ATOM string as XML document.");
160
161 hr = ParseAtomDocument(pixdAtom, &pNewFeed);
162 AtomExitOnFailure(hr, "Failed to parse ATOM document.");
163
164 *ppFeed = pNewFeed;
165 pNewFeed = NULL;
166
167LExit:
168 ReleaseAtomFeed(pNewFeed);
169 ReleaseObject(pixdAtom);
170
171 return hr;
172}
173
174
175/********************************************************************
176 AtomParseFromDocument - parses out an ATOM feed from an XML document.
177
178*********************************************************************/
179extern "C" HRESULT DAPI AtomParseFromDocument(
180 __in IXMLDOMDocument* pixdDocument,
181 __out ATOM_FEED **ppFeed
182 )
183{
184 Assert(pixdDocument);
185 Assert(ppFeed);
186
187 HRESULT hr = S_OK;
188 ATOM_FEED *pNewFeed = NULL;
189
190 hr = ParseAtomDocument(pixdDocument, &pNewFeed);
191 AtomExitOnFailure(hr, "Failed to parse ATOM document.");
192
193 *ppFeed = pNewFeed;
194 pNewFeed = NULL;
195
196LExit:
197 ReleaseAtomFeed(pNewFeed);
198
199 return hr;
200}
201
202
203/********************************************************************
204 AtomFreeFeed - parses out an ATOM feed from a string.
205
206*********************************************************************/
207extern "C" void DAPI AtomFreeFeed(
208 __in_xcount(pFeed->cItems) ATOM_FEED* pFeed
209 )
210{
211 if (pFeed)
212 {
213 FreeAtomUnknownElementList(pFeed->pUnknownElements);
214 ReleaseObject(pFeed->pixn);
215
216 for (DWORD i = 0; i < pFeed->cLinks; ++i)
217 {
218 FreeAtomLink(pFeed->rgLinks + i);
219 }
220 ReleaseMem(pFeed->rgLinks);
221
222 for (DWORD i = 0; i < pFeed->cEntries; ++i)
223 {
224 FreeAtomEntry(pFeed->rgEntries + i);
225 }
226 ReleaseMem(pFeed->rgEntries);
227
228 for (DWORD i = 0; i < pFeed->cCategories; ++i)
229 {
230 FreeAtomCategory(pFeed->rgCategories + i);
231 }
232 ReleaseMem(pFeed->rgCategories);
233
234 for (DWORD i = 0; i < pFeed->cAuthors; ++i)
235 {
236 FreeAtomAuthor(pFeed->rgAuthors + i);
237 }
238 ReleaseMem(pFeed->rgAuthors);
239
240 ReleaseStr(pFeed->wzGenerator);
241 ReleaseStr(pFeed->wzIcon);
242 ReleaseStr(pFeed->wzId);
243 ReleaseStr(pFeed->wzLogo);
244 ReleaseStr(pFeed->wzSubtitle);
245 ReleaseStr(pFeed->wzTitle);
246
247 MemFree(pFeed);
248 }
249}
250
251
252/********************************************************************
253 ParseAtomDocument - parses out an ATOM feed from a loaded XML DOM document.
254
255*********************************************************************/
256static HRESULT ParseAtomDocument(
257 __in IXMLDOMDocument *pixd,
258 __out ATOM_FEED **ppFeed
259 )
260{
261 Assert(pixd);
262 Assert(ppFeed);
263
264 HRESULT hr = S_OK;
265 IXMLDOMElement *pFeedElement = NULL;
266
267 ATOM_FEED *pNewFeed = NULL;
268
269 //
270 // Get the document element and start processing feeds.
271 //
272 hr = pixd->get_documentElement(&pFeedElement);
273 AtomExitOnFailure(hr, "failed get_documentElement in ParseAtomDocument");
274
275 hr = ParseAtomFeed(pFeedElement, &pNewFeed);
276 AtomExitOnFailure(hr, "Failed to parse ATOM feed.");
277
278 if (S_FALSE == hr)
279 {
280 hr = S_OK;
281 }
282
283 *ppFeed = pNewFeed;
284 pNewFeed = NULL;
285
286LExit:
287 ReleaseObject(pFeedElement);
288
289 ReleaseAtomFeed(pNewFeed);
290
291 return hr;
292}
293
294
295/********************************************************************
296 ParseAtomFeed - parses out an ATOM feed from a loaded XML DOM element.
297
298*********************************************************************/
299static HRESULT ParseAtomFeed(
300 __in IXMLDOMNode *pixnFeed,
301 __out ATOM_FEED **ppFeed
302 )
303{
304 Assert(pixnFeed);
305 Assert(ppFeed);
306
307 HRESULT hr = S_OK;
308 IXMLDOMNodeList *pNodeList = NULL;
309
310 ATOM_FEED *pNewFeed = NULL;
311 DWORD cAuthors = 0;
312 DWORD cCategories = 0;
313 DWORD cEntries = 0;
314 DWORD cLinks = 0;
315
316 IXMLDOMNode *pNode = NULL;
317 BSTR bstrNodeName = NULL;
318
319 // First, allocate the new feed and all the possible sub elements.
320 pNewFeed = (ATOM_FEED*)MemAlloc(sizeof(ATOM_FEED), TRUE);
321 AtomExitOnNull(pNewFeed, hr, E_OUTOFMEMORY, "Failed to allocate ATOM feed structure.");
322
323 pNewFeed->pixn = pixnFeed;
324 pNewFeed->pixn->AddRef();
325
326 hr = AllocateAtomType<ATOM_AUTHOR>(pixnFeed, L"author", &pNewFeed->rgAuthors, &pNewFeed->cAuthors);
327 AtomExitOnFailure(hr, "Failed to allocate ATOM feed authors.");
328
329 hr = AllocateAtomType<ATOM_CATEGORY>(pixnFeed, L"category", &pNewFeed->rgCategories, &pNewFeed->cCategories);
330 AtomExitOnFailure(hr, "Failed to allocate ATOM feed categories.");
331
332 hr = AllocateAtomType<ATOM_ENTRY>(pixnFeed, L"entry", &pNewFeed->rgEntries, &pNewFeed->cEntries);
333 AtomExitOnFailure(hr, "Failed to allocate ATOM feed entries.");
334
335 hr = AllocateAtomType<ATOM_LINK>(pixnFeed, L"link", &pNewFeed->rgLinks, &pNewFeed->cLinks);
336 AtomExitOnFailure(hr, "Failed to allocate ATOM feed links.");
337
338 // Second, process the elements under a feed.
339 hr = pixnFeed->get_childNodes(&pNodeList);
340 AtomExitOnFailure(hr, "Failed to get child nodes of ATOM feed element.");
341
342 while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName)))
343 {
344 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"generator", -1))
345 {
346 hr = AssignString(&pNewFeed->wzGenerator, pNode);
347 AtomExitOnFailure(hr, "Failed to allocate ATOM feed generator.");
348 }
349 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"icon", -1))
350 {
351 hr = AssignString(&pNewFeed->wzIcon, pNode);
352 AtomExitOnFailure(hr, "Failed to allocate ATOM feed icon.");
353 }
354 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"id", -1))
355 {
356 hr = AssignString(&pNewFeed->wzId, pNode);
357 AtomExitOnFailure(hr, "Failed to allocate ATOM feed id.");
358 }
359 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"logo", -1))
360 {
361 hr = AssignString(&pNewFeed->wzLogo, pNode);
362 AtomExitOnFailure(hr, "Failed to allocate ATOM feed logo.");
363 }
364 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"subtitle", -1))
365 {
366 hr = AssignString(&pNewFeed->wzSubtitle, pNode);
367 AtomExitOnFailure(hr, "Failed to allocate ATOM feed subtitle.");
368 }
369 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"title", -1))
370 {
371 hr = AssignString(&pNewFeed->wzTitle, pNode);
372 AtomExitOnFailure(hr, "Failed to allocate ATOM feed title.");
373 }
374 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"updated", -1))
375 {
376 hr = AssignDateTime(&pNewFeed->ftUpdated, pNode);
377 AtomExitOnFailure(hr, "Failed to allocate ATOM feed updated.");
378 }
379 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"author", -1))
380 {
381 hr = ParseAtomAuthor(pNode, &pNewFeed->rgAuthors[cAuthors]);
382 AtomExitOnFailure(hr, "Failed to parse ATOM author.");
383
384 ++cAuthors;
385 }
386 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"category", -1))
387 {
388 hr = ParseAtomCategory(pNode, &pNewFeed->rgCategories[cCategories]);
389 AtomExitOnFailure(hr, "Failed to parse ATOM category.");
390
391 ++cCategories;
392 }
393 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"entry", -1))
394 {
395 hr = ParseAtomEntry(pNode, &pNewFeed->rgEntries[cEntries]);
396 AtomExitOnFailure(hr, "Failed to parse ATOM entry.");
397
398 ++cEntries;
399 }
400 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"link", -1))
401 {
402 hr = ParseAtomLink(pNode, &pNewFeed->rgLinks[cLinks]);
403 AtomExitOnFailure(hr, "Failed to parse ATOM link.");
404
405 ++cLinks;
406 }
407 else
408 {
409 hr = ParseAtomUnknownElement(pNode, &pNewFeed->pUnknownElements);
410 AtomExitOnFailure(hr, "Failed to parse unknown ATOM feed element: %ls", bstrNodeName);
411 }
412
413 ReleaseNullBSTR(bstrNodeName);
414 ReleaseNullObject(pNode);
415 }
416
417 if (!pNewFeed->wzId || !*pNewFeed->wzId)
418 {
419 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
420 AtomExitOnRootFailure(hr, "Failed to find required feed/id element.");
421 }
422 else if (!pNewFeed->wzTitle || !*pNewFeed->wzTitle)
423 {
424 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
425 AtomExitOnRootFailure(hr, "Failed to find required feed/title element.");
426 }
427 else if (0 == pNewFeed->ftUpdated.dwHighDateTime && 0 == pNewFeed->ftUpdated.dwLowDateTime)
428 {
429 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
430 AtomExitOnRootFailure(hr, "Failed to find required feed/updated element.");
431 }
432
433 *ppFeed = pNewFeed;
434 pNewFeed = NULL;
435
436LExit:
437 ReleaseBSTR(bstrNodeName);
438 ReleaseObject(pNode);
439 ReleaseObject(pNodeList);
440
441 ReleaseAtomFeed(pNewFeed);
442
443 return hr;
444}
445
446
447/********************************************************************
448 AllocateAtomType - allocates enough space for all of the ATOM elements
449 of a particular type under a particular node.
450
451*********************************************************************/
452template<class T> static HRESULT AllocateAtomType(
453 __in IXMLDOMNode* pixnParent,
454 __in LPCWSTR wzT,
455 __out T** pprgT,
456 __out DWORD* pcT
457 )
458{
459 HRESULT hr = S_OK;
460 IXMLDOMNodeList *pNodeList = NULL;
461
462 long cT = 0;
463 T* prgT = NULL;
464
465 hr = XmlSelectNodes(pixnParent, wzT, &pNodeList);
466 AtomExitOnFailure(hr, "Failed to select all ATOM %ls.", wzT);
467
468 if (S_OK == hr)
469 {
470 hr = pNodeList->get_length(&cT);
471 AtomExitOnFailure(hr, "Failed to count the number of ATOM %ls.", wzT);
472
473 if (cT == 0)
474 {
475 ExitFunction();
476 }
477
478 prgT = static_cast<T*>(MemAlloc(sizeof(T) * cT, TRUE));
479 AtomExitOnNull(prgT, hr, E_OUTOFMEMORY, "Failed to allocate ATOM.");
480
481 *pcT = cT;
482 *pprgT = prgT;
483 prgT = NULL;
484 }
485 else
486 {
487 *pprgT = NULL;
488 *pcT = 0;
489 }
490
491LExit:
492 ReleaseMem(prgT);
493 ReleaseObject(pNodeList);
494
495 return hr;
496}
497
498
499/********************************************************************
500 ParseAtomAuthor - parses out an ATOM author from a loaded XML DOM node.
501
502*********************************************************************/
503static HRESULT ParseAtomAuthor(
504 __in IXMLDOMNode* pixnAuthor,
505 __in ATOM_AUTHOR* pAuthor
506 )
507{
508 HRESULT hr = S_OK;
509
510 IXMLDOMNodeList *pNodeList = NULL;
511 IXMLDOMNode *pNode = NULL;
512 BSTR bstrNodeName = NULL;
513
514 hr = pixnAuthor->get_childNodes(&pNodeList);
515 AtomExitOnFailure(hr, "Failed to get child nodes of ATOM author element.");
516
517 while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName)))
518 {
519 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"name", -1))
520 {
521 hr = AssignString(&pAuthor->wzName, pNode);
522 AtomExitOnFailure(hr, "Failed to allocate ATOM author name.");
523 }
524 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"email", -1))
525 {
526 hr = AssignString(&pAuthor->wzEmail, pNode);
527 AtomExitOnFailure(hr, "Failed to allocate ATOM author email.");
528 }
529 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"uri", -1))
530 {
531 hr = AssignString(&pAuthor->wzUrl, pNode);
532 AtomExitOnFailure(hr, "Failed to allocate ATOM author uri.");
533 }
534
535 ReleaseNullBSTR(bstrNodeName);
536 ReleaseNullObject(pNode);
537 }
538 AtomExitOnFailure(hr, "Failed to process all ATOM author elements.");
539
540 hr = S_OK;
541
542LExit:
543 ReleaseBSTR(bstrNodeName);
544 ReleaseObject(pNode);
545 ReleaseObject(pNodeList);
546
547 return hr;
548}
549
550
551/********************************************************************
552 ParseAtomCategory - parses out an ATOM category from a loaded XML DOM node.
553
554*********************************************************************/
555static HRESULT ParseAtomCategory(
556 __in IXMLDOMNode* pixnCategory,
557 __in ATOM_CATEGORY* pCategory
558 )
559{
560 HRESULT hr = S_OK;
561
562 IXMLDOMNamedNodeMap* pixnnmAttributes = NULL;
563 IXMLDOMNodeList *pNodeList = NULL;
564 IXMLDOMNode *pNode = NULL;
565 BSTR bstrNodeName = NULL;
566
567 // Process attributes first.
568 hr = pixnCategory->get_attributes(&pixnnmAttributes);
569 AtomExitOnFailure(hr, "Failed get attributes on ATOM unknown element.");
570
571 while (S_OK == (hr = XmlNextAttribute(pixnnmAttributes, &pNode, &bstrNodeName)))
572 {
573 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"label", -1))
574 {
575 hr = AssignString(&pCategory->wzLabel, pNode);
576 AtomExitOnFailure(hr, "Failed to allocate ATOM category label.");
577 }
578 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"scheme", -1))
579 {
580 hr = AssignString(&pCategory->wzScheme, pNode);
581 AtomExitOnFailure(hr, "Failed to allocate ATOM category scheme.");
582 }
583 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"term", -1))
584 {
585 hr = AssignString(&pCategory->wzTerm, pNode);
586 AtomExitOnFailure(hr, "Failed to allocate ATOM category term.");
587 }
588
589 ReleaseNullBSTR(bstrNodeName);
590 ReleaseNullObject(pNode);
591 }
592 AtomExitOnFailure(hr, "Failed to process all ATOM category attributes.");
593
594 // Process elements second.
595 hr = pixnCategory->get_childNodes(&pNodeList);
596 AtomExitOnFailure(hr, "Failed to get child nodes of ATOM category element.");
597
598 while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName)))
599 {
600 hr = ParseAtomUnknownElement(pNode, &pCategory->pUnknownElements);
601 AtomExitOnFailure(hr, "Failed to parse unknown ATOM category element: %ls", bstrNodeName);
602
603 ReleaseNullBSTR(bstrNodeName);
604 ReleaseNullObject(pNode);
605 }
606 AtomExitOnFailure(hr, "Failed to process all ATOM category elements.");
607
608 hr = S_OK;
609
610LExit:
611 ReleaseBSTR(bstrNodeName);
612 ReleaseObject(pNode);
613 ReleaseObject(pNodeList);
614 ReleaseObject(pixnnmAttributes);
615
616 return hr;
617}
618
619
620/********************************************************************
621 ParseAtomContent - parses out an ATOM content from a loaded XML DOM node.
622
623*********************************************************************/
624static HRESULT ParseAtomContent(
625 __in IXMLDOMNode* pixnContent,
626 __in ATOM_CONTENT* pContent
627 )
628{
629 HRESULT hr = S_OK;
630
631 IXMLDOMNamedNodeMap* pixnnmAttributes = NULL;
632 IXMLDOMNodeList *pNodeList = NULL;
633 IXMLDOMNode *pNode = NULL;
634 BSTR bstrNodeName = NULL;
635
636 // Process attributes first.
637 hr = pixnContent->get_attributes(&pixnnmAttributes);
638 AtomExitOnFailure(hr, "Failed get attributes on ATOM unknown element.");
639
640 while (S_OK == (hr = XmlNextAttribute(pixnnmAttributes, &pNode, &bstrNodeName)))
641 {
642 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"type", -1))
643 {
644 hr = AssignString(&pContent->wzType, pNode);
645 AtomExitOnFailure(hr, "Failed to allocate ATOM content type.");
646 }
647 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"url", -1))
648 {
649 hr = AssignString(&pContent->wzUrl, pNode);
650 AtomExitOnFailure(hr, "Failed to allocate ATOM content scheme.");
651 }
652
653 ReleaseNullBSTR(bstrNodeName);
654 ReleaseNullObject(pNode);
655 }
656 AtomExitOnFailure(hr, "Failed to process all ATOM content attributes.");
657
658 // Process elements second.
659 hr = pixnContent->get_childNodes(&pNodeList);
660 AtomExitOnFailure(hr, "Failed to get child nodes of ATOM content element.");
661
662 while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName)))
663 {
664 hr = ParseAtomUnknownElement(pNode, &pContent->pUnknownElements);
665 AtomExitOnFailure(hr, "Failed to parse unknown ATOM content element: %ls", bstrNodeName);
666
667 ReleaseNullBSTR(bstrNodeName);
668 ReleaseNullObject(pNode);
669 }
670 AtomExitOnFailure(hr, "Failed to process all ATOM content elements.");
671
672 hr = AssignString(&pContent->wzValue, pixnContent);
673 AtomExitOnFailure(hr, "Failed to allocate ATOM content value.");
674
675LExit:
676 ReleaseBSTR(bstrNodeName);
677 ReleaseObject(pNode);
678 ReleaseObject(pNodeList);
679 ReleaseObject(pixnnmAttributes);
680
681 return hr;
682}
683
684
685/********************************************************************
686 ParseAtomEntry - parses out an ATOM entry from a loaded XML DOM node.
687
688*********************************************************************/
689static HRESULT ParseAtomEntry(
690 __in IXMLDOMNode* pixnEntry,
691 __in ATOM_ENTRY* pEntry
692 )
693{
694 HRESULT hr = S_OK;
695
696 IXMLDOMNamedNodeMap* pixnnmAttributes = NULL;
697 IXMLDOMNodeList *pNodeList = NULL;
698 IXMLDOMNode *pNode = NULL;
699 BSTR bstrNodeName = NULL;
700
701 DWORD cAuthors = 0;
702 DWORD cCategories = 0;
703 DWORD cLinks = 0;
704
705 pEntry->pixn = pixnEntry;
706 pEntry->pixn->AddRef();
707
708 // First, allocate all the possible sub elements.
709 hr = AllocateAtomType<ATOM_AUTHOR>(pixnEntry, L"author", &pEntry->rgAuthors, &pEntry->cAuthors);
710 AtomExitOnFailure(hr, "Failed to allocate ATOM entry authors.");
711
712 hr = AllocateAtomType<ATOM_CATEGORY>(pixnEntry, L"category", &pEntry->rgCategories, &pEntry->cCategories);
713 AtomExitOnFailure(hr, "Failed to allocate ATOM entry categories.");
714
715 hr = AllocateAtomType<ATOM_LINK>(pixnEntry, L"link", &pEntry->rgLinks, &pEntry->cLinks);
716 AtomExitOnFailure(hr, "Failed to allocate ATOM entry links.");
717
718 // Second, process elements.
719 hr = pixnEntry->get_childNodes(&pNodeList);
720 AtomExitOnFailure(hr, "Failed to get child nodes of ATOM entry element.");
721
722 while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName)))
723 {
724 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"id", -1))
725 {
726 hr = AssignString(&pEntry->wzId, pNode);
727 AtomExitOnFailure(hr, "Failed to allocate ATOM entry id.");
728 }
729 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"summary", -1))
730 {
731 hr = AssignString(&pEntry->wzSummary, pNode);
732 AtomExitOnFailure(hr, "Failed to allocate ATOM entry summary.");
733 }
734 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"title", -1))
735 {
736 hr = AssignString(&pEntry->wzTitle, pNode);
737 AtomExitOnFailure(hr, "Failed to allocate ATOM entry title.");
738 }
739 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"published", -1))
740 {
741 hr = AssignDateTime(&pEntry->ftPublished, pNode);
742 AtomExitOnFailure(hr, "Failed to allocate ATOM entry published.");
743 }
744 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"updated", -1))
745 {
746 hr = AssignDateTime(&pEntry->ftUpdated, pNode);
747 AtomExitOnFailure(hr, "Failed to allocate ATOM entry updated.");
748 }
749 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"author", -1))
750 {
751 hr = ParseAtomAuthor(pNode, &pEntry->rgAuthors[cAuthors]);
752 AtomExitOnFailure(hr, "Failed to parse ATOM entry author.");
753
754 ++cAuthors;
755 }
756 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"category", -1))
757 {
758 hr = ParseAtomCategory(pNode, &pEntry->rgCategories[cCategories]);
759 AtomExitOnFailure(hr, "Failed to parse ATOM entry category.");
760
761 ++cCategories;
762 }
763 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"content", -1))
764 {
765 if (NULL != pEntry->pContent)
766 {
767 hr = E_UNEXPECTED;
768 AtomExitOnFailure(hr, "Cannot have two content elements in ATOM entry.");
769 }
770
771 pEntry->pContent = static_cast<ATOM_CONTENT*>(MemAlloc(sizeof(ATOM_CONTENT), TRUE));
772 AtomExitOnNull(pEntry->pContent, hr, E_OUTOFMEMORY, "Failed to allocate ATOM entry content.");
773
774 hr = ParseAtomContent(pNode, pEntry->pContent);
775 AtomExitOnFailure(hr, "Failed to parse ATOM entry content.");
776 }
777 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"link", -1))
778 {
779 hr = ParseAtomLink(pNode, &pEntry->rgLinks[cLinks]);
780 AtomExitOnFailure(hr, "Failed to parse ATOM entry link.");
781
782 ++cLinks;
783 }
784 else
785 {
786 hr = ParseAtomUnknownElement(pNode, &pEntry->pUnknownElements);
787 AtomExitOnFailure(hr, "Failed to parse unknown ATOM entry element: %ls", bstrNodeName);
788 }
789
790 ReleaseNullBSTR(bstrNodeName);
791 ReleaseNullObject(pNode);
792 }
793 AtomExitOnFailure(hr, "Failed to process all ATOM entry elements.");
794
795 if (!pEntry->wzId || !*pEntry->wzId)
796 {
797 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
798 AtomExitOnRootFailure(hr, "Failed to find required feed/entry/id element.");
799 }
800 else if (!pEntry->wzTitle || !*pEntry->wzTitle)
801 {
802 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
803 AtomExitOnRootFailure(hr, "Failed to find required feed/entry/title element.");
804 }
805 else if (0 == pEntry->ftUpdated.dwHighDateTime && 0 == pEntry->ftUpdated.dwLowDateTime)
806 {
807 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
808 AtomExitOnRootFailure(hr, "Failed to find required feed/entry/updated element.");
809 }
810
811 hr = S_OK;
812
813LExit:
814 ReleaseBSTR(bstrNodeName);
815 ReleaseObject(pNode);
816 ReleaseObject(pNodeList);
817 ReleaseObject(pixnnmAttributes);
818
819 return hr;
820}
821
822
823/********************************************************************
824 ParseAtomLink - parses out an ATOM link from a loaded XML DOM node.
825
826*********************************************************************/
827static HRESULT ParseAtomLink(
828 __in IXMLDOMNode* pixnLink,
829 __in ATOM_LINK* pLink
830 )
831{
832 HRESULT hr = S_OK;
833
834 IXMLDOMNamedNodeMap* pixnnmAttributes = NULL;
835 IXMLDOMNodeList *pNodeList = NULL;
836 IXMLDOMNode *pNode = NULL;
837 BSTR bstrNodeName = NULL;
838
839 // Process attributes first.
840 hr = pixnLink->get_attributes(&pixnnmAttributes);
841 AtomExitOnFailure(hr, "Failed get attributes for ATOM link.");
842
843 while (S_OK == (hr = XmlNextAttribute(pixnnmAttributes, &pNode, &bstrNodeName)))
844 {
845 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"rel", -1))
846 {
847 hr = AssignString(&pLink->wzRel, pNode);
848 AtomExitOnFailure(hr, "Failed to allocate ATOM link rel.");
849 }
850 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"href", -1))
851 {
852 hr = AssignString(&pLink->wzUrl, pNode);
853 AtomExitOnFailure(hr, "Failed to allocate ATOM link href.");
854 }
855 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"length", -1))
856 {
857 hr = XmlGetAttributeLargeNumber(pixnLink, bstrNodeName, &pLink->dw64Length);
858 if (E_INVALIDARG == hr)
859 {
860 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
861 }
862 AtomExitOnFailure(hr, "Failed to parse ATOM link length.");
863 }
864 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"title", -1))
865 {
866 hr = AssignString(&pLink->wzTitle, pNode);
867 AtomExitOnFailure(hr, "Failed to allocate ATOM link title.");
868 }
869 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"type", -1))
870 {
871 hr = AssignString(&pLink->wzType, pNode);
872 AtomExitOnFailure(hr, "Failed to allocate ATOM link type.");
873 }
874 else
875 {
876 hr = ParseAtomUnknownAttribute(pNode, &pLink->pUnknownAttributes);
877 AtomExitOnFailure(hr, "Failed to parse unknown ATOM link attribute: %ls", bstrNodeName);
878 }
879
880 ReleaseNullBSTR(bstrNodeName);
881 ReleaseNullObject(pNode);
882 }
883 AtomExitOnFailure(hr, "Failed to process all ATOM link attributes.");
884
885 // Process elements second.
886 hr = pixnLink->get_childNodes(&pNodeList);
887 AtomExitOnFailure(hr, "Failed to get child nodes of ATOM link element.");
888
889 while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName)))
890 {
891 hr = ParseAtomUnknownElement(pNode, &pLink->pUnknownElements);
892 AtomExitOnFailure(hr, "Failed to parse unknown ATOM link element: %ls", bstrNodeName);
893
894 ReleaseNullBSTR(bstrNodeName);
895 ReleaseNullObject(pNode);
896 }
897 AtomExitOnFailure(hr, "Failed to process all ATOM link elements.");
898
899 hr = AssignString(&pLink->wzValue, pixnLink);
900 AtomExitOnFailure(hr, "Failed to allocate ATOM link value.");
901
902LExit:
903 ReleaseBSTR(bstrNodeName);
904 ReleaseObject(pNode);
905 ReleaseObject(pNodeList);
906 ReleaseObject(pixnnmAttributes);
907
908 return hr;
909}
910
911
912/********************************************************************
913 ParseAtomUnknownElement - parses out an unknown item from the ATOM feed from a loaded XML DOM node.
914
915*********************************************************************/
916static HRESULT ParseAtomUnknownElement(
917 __in IXMLDOMNode *pNode,
918 __inout ATOM_UNKNOWN_ELEMENT** ppUnknownElement
919 )
920{
921 Assert(ppUnknownElement);
922
923 HRESULT hr = S_OK;
924 BSTR bstrNodeNamespace = NULL;
925 BSTR bstrNodeName = NULL;
926 BSTR bstrNodeValue = NULL;
927 IXMLDOMNamedNodeMap* pixnnmAttributes = NULL;
928 IXMLDOMNode* pixnAttribute = NULL;
929 ATOM_UNKNOWN_ELEMENT* pNewUnknownElement;
930
931 pNewUnknownElement = (ATOM_UNKNOWN_ELEMENT*)MemAlloc(sizeof(ATOM_UNKNOWN_ELEMENT), TRUE);
932 AtomExitOnNull(pNewUnknownElement, hr, E_OUTOFMEMORY, "Failed to allocate unknown element.");
933
934 hr = pNode->get_namespaceURI(&bstrNodeNamespace);
935 if (S_OK == hr)
936 {
937 hr = StrAllocString(&pNewUnknownElement->wzNamespace, bstrNodeNamespace, 0);
938 AtomExitOnFailure(hr, "Failed to allocate ATOM unknown element namespace.");
939 }
940 else if (S_FALSE == hr)
941 {
942 hr = S_OK;
943 }
944 AtomExitOnFailure(hr, "Failed to get unknown element namespace.");
945
946 hr = pNode->get_baseName(&bstrNodeName);
947 AtomExitOnFailure(hr, "Failed to get unknown element name.");
948
949 hr = StrAllocString(&pNewUnknownElement->wzElement, bstrNodeName, 0);
950 AtomExitOnFailure(hr, "Failed to allocate ATOM unknown element name.");
951
952 hr = XmlGetText(pNode, &bstrNodeValue);
953 AtomExitOnFailure(hr, "Failed to get unknown element value.");
954
955 hr = StrAllocString(&pNewUnknownElement->wzValue, bstrNodeValue, 0);
956 AtomExitOnFailure(hr, "Failed to allocate ATOM unknown element value.");
957
958 hr = pNode->get_attributes(&pixnnmAttributes);
959 AtomExitOnFailure(hr, "Failed get attributes on ATOM unknown element.");
960
961 while (S_OK == (hr = pixnnmAttributes->nextNode(&pixnAttribute)))
962 {
963 hr = ParseAtomUnknownAttribute(pixnAttribute, &pNewUnknownElement->pAttributes);
964 AtomExitOnFailure(hr, "Failed to parse attribute on ATOM unknown element.");
965
966 ReleaseNullObject(pixnAttribute);
967 }
968
969 if (S_FALSE == hr)
970 {
971 hr = S_OK;
972 }
973 AtomExitOnFailure(hr, "Failed to enumerate all attributes on ATOM unknown element.");
974
975 ATOM_UNKNOWN_ELEMENT** ppTail = ppUnknownElement;
976 while (*ppTail)
977 {
978 ppTail = &(*ppTail)->pNext;
979 }
980
981 *ppTail = pNewUnknownElement;
982 pNewUnknownElement = NULL;
983
984LExit:
985 FreeAtomUnknownElementList(pNewUnknownElement);
986
987 ReleaseBSTR(bstrNodeNamespace);
988 ReleaseBSTR(bstrNodeName);
989 ReleaseBSTR(bstrNodeValue);
990 ReleaseObject(pixnnmAttributes);
991 ReleaseObject(pixnAttribute);
992
993 return hr;
994}
995
996
997/********************************************************************
998 ParseAtomUnknownAttribute - parses out attribute from an unknown element
999
1000*********************************************************************/
1001static HRESULT ParseAtomUnknownAttribute(
1002 __in IXMLDOMNode *pNode,
1003 __inout ATOM_UNKNOWN_ATTRIBUTE** ppUnknownAttribute
1004 )
1005{
1006 Assert(ppUnknownAttribute);
1007
1008 HRESULT hr = S_OK;
1009 BSTR bstrNodeNamespace = NULL;
1010 BSTR bstrNodeName = NULL;
1011 BSTR bstrNodeValue = NULL;
1012 ATOM_UNKNOWN_ATTRIBUTE* pNewUnknownAttribute;
1013
1014 pNewUnknownAttribute = (ATOM_UNKNOWN_ATTRIBUTE*)MemAlloc(sizeof(ATOM_UNKNOWN_ATTRIBUTE), TRUE);
1015 AtomExitOnNull(pNewUnknownAttribute, hr, E_OUTOFMEMORY, "Failed to allocate unknown attribute.");
1016
1017 hr = pNode->get_namespaceURI(&bstrNodeNamespace);
1018 if (S_OK == hr)
1019 {
1020 hr = StrAllocString(&pNewUnknownAttribute->wzNamespace, bstrNodeNamespace, 0);
1021 AtomExitOnFailure(hr, "Failed to allocate ATOM unknown attribute namespace.");
1022 }
1023 else if (S_FALSE == hr)
1024 {
1025 hr = S_OK;
1026 }
1027 AtomExitOnFailure(hr, "Failed to get unknown attribute namespace.");
1028
1029 hr = pNode->get_baseName(&bstrNodeName);
1030 AtomExitOnFailure(hr, "Failed to get unknown attribute name.");
1031
1032 hr = StrAllocString(&pNewUnknownAttribute->wzAttribute, bstrNodeName, 0);
1033 AtomExitOnFailure(hr, "Failed to allocate ATOM unknown attribute name.");
1034
1035 hr = XmlGetText(pNode, &bstrNodeValue);
1036 AtomExitOnFailure(hr, "Failed to get unknown attribute value.");
1037
1038 hr = StrAllocString(&pNewUnknownAttribute->wzValue, bstrNodeValue, 0);
1039 AtomExitOnFailure(hr, "Failed to allocate ATOM unknown attribute value.");
1040
1041 ATOM_UNKNOWN_ATTRIBUTE** ppTail = ppUnknownAttribute;
1042 while (*ppTail)
1043 {
1044 ppTail = &(*ppTail)->pNext;
1045 }
1046
1047 *ppTail = pNewUnknownAttribute;
1048 pNewUnknownAttribute = NULL;
1049
1050LExit:
1051 FreeAtomUnknownAttributeList(pNewUnknownAttribute);
1052
1053 ReleaseBSTR(bstrNodeNamespace);
1054 ReleaseBSTR(bstrNodeName);
1055 ReleaseBSTR(bstrNodeValue);
1056
1057 return hr;
1058}
1059
1060
1061/********************************************************************
1062 AssignDateTime - assigns the value of a node to a FILETIME struct.
1063
1064*********************************************************************/
1065static HRESULT AssignDateTime(
1066 __in FILETIME* pft,
1067 __in IXMLDOMNode* pNode
1068 )
1069{
1070 HRESULT hr = S_OK;
1071 BSTR bstrValue = NULL;
1072
1073 if (0 != pft->dwHighDateTime || 0 != pft->dwLowDateTime)
1074 {
1075 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
1076 AtomExitOnRootFailure(hr, "Already process this datetime value.");
1077 }
1078
1079 hr = XmlGetText(pNode, &bstrValue);
1080 AtomExitOnFailure(hr, "Failed to get value.");
1081
1082 if (S_OK == hr)
1083 {
1084 hr = TimeFromString3339(bstrValue, pft);
1085 AtomExitOnFailure(hr, "Failed to convert value to time.");
1086 }
1087 else
1088 {
1089 ZeroMemory(pft, sizeof(FILETIME));
1090 hr = S_OK;
1091 }
1092
1093LExit:
1094 ReleaseBSTR(bstrValue);
1095
1096 return hr;
1097}
1098
1099
1100/********************************************************************
1101 AssignString - assigns the value of a node to a dynamic string.
1102
1103*********************************************************************/
1104static HRESULT AssignString(
1105 __out_z LPWSTR* pwzValue,
1106 __in IXMLDOMNode* pNode
1107 )
1108{
1109 HRESULT hr = S_OK;
1110 BSTR bstrValue = NULL;
1111
1112 if (pwzValue && *pwzValue)
1113 {
1114 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
1115 AtomExitOnRootFailure(hr, "Already processed this value.");
1116 }
1117
1118 hr = XmlGetText(pNode, &bstrValue);
1119 AtomExitOnFailure(hr, "Failed to get value.");
1120
1121 if (S_OK == hr)
1122 {
1123 hr = StrAllocString(pwzValue, bstrValue, 0);
1124 AtomExitOnFailure(hr, "Failed to allocate value.");
1125 }
1126 else
1127 {
1128 ReleaseNullStr(pwzValue);
1129 hr = S_OK;
1130 }
1131
1132LExit:
1133 ReleaseBSTR(bstrValue);
1134
1135 return hr;
1136}
1137
1138
1139/********************************************************************
1140 FreeAtomAuthor - releases all of the memory used by an ATOM author.
1141
1142*********************************************************************/
1143static void FreeAtomAuthor(
1144 __in_opt ATOM_AUTHOR* pAuthor
1145 )
1146{
1147 if (pAuthor)
1148 {
1149 ReleaseStr(pAuthor->wzUrl);
1150 ReleaseStr(pAuthor->wzEmail);
1151 ReleaseStr(pAuthor->wzName);
1152 }
1153}
1154
1155
1156/********************************************************************
1157 FreeAtomCategory - releases all of the memory used by an ATOM category.
1158
1159*********************************************************************/
1160static void FreeAtomCategory(
1161 __in_opt ATOM_CATEGORY* pCategory
1162 )
1163{
1164 if (pCategory)
1165 {
1166 FreeAtomUnknownElementList(pCategory->pUnknownElements);
1167
1168 ReleaseStr(pCategory->wzTerm);
1169 ReleaseStr(pCategory->wzScheme);
1170 ReleaseStr(pCategory->wzLabel);
1171 }
1172}
1173
1174
1175/********************************************************************
1176 FreeAtomContent - releases all of the memory used by an ATOM content.
1177
1178*********************************************************************/
1179static void FreeAtomContent(
1180 __in_opt ATOM_CONTENT* pContent
1181 )
1182{
1183 if (pContent)
1184 {
1185 FreeAtomUnknownElementList(pContent->pUnknownElements);
1186
1187 ReleaseStr(pContent->wzValue);
1188 ReleaseStr(pContent->wzUrl);
1189 ReleaseStr(pContent->wzType);
1190 }
1191}
1192
1193
1194/********************************************************************
1195 FreeAtomEntry - releases all of the memory used by an ATOM entry.
1196
1197*********************************************************************/
1198static void FreeAtomEntry(
1199 __in_opt ATOM_ENTRY* pEntry
1200 )
1201{
1202 if (pEntry)
1203 {
1204 FreeAtomUnknownElementList(pEntry->pUnknownElements);
1205 ReleaseObject(pEntry->pixn);
1206
1207 for (DWORD i = 0; i < pEntry->cLinks; ++i)
1208 {
1209 FreeAtomLink(pEntry->rgLinks + i);
1210 }
1211 ReleaseMem(pEntry->rgLinks);
1212
1213 for (DWORD i = 0; i < pEntry->cCategories; ++i)
1214 {
1215 FreeAtomCategory(pEntry->rgCategories + i);
1216 }
1217 ReleaseMem(pEntry->rgCategories);
1218
1219 for (DWORD i = 0; i < pEntry->cAuthors; ++i)
1220 {
1221 FreeAtomAuthor(pEntry->rgAuthors + i);
1222 }
1223 ReleaseMem(pEntry->rgAuthors);
1224
1225 FreeAtomContent(pEntry->pContent);
1226 ReleaseMem(pEntry->pContent);
1227
1228 ReleaseStr(pEntry->wzTitle);
1229 ReleaseStr(pEntry->wzSummary);
1230 ReleaseStr(pEntry->wzId);
1231 }
1232}
1233
1234
1235/********************************************************************
1236 FreeAtomLink - releases all of the memory used by an ATOM link.
1237
1238*********************************************************************/
1239static void FreeAtomLink(
1240 __in_opt ATOM_LINK* pLink
1241 )
1242{
1243 if (pLink)
1244 {
1245 FreeAtomUnknownElementList(pLink->pUnknownElements);
1246 FreeAtomUnknownAttributeList(pLink->pUnknownAttributes);
1247
1248 ReleaseStr(pLink->wzValue);
1249 ReleaseStr(pLink->wzUrl);
1250 ReleaseStr(pLink->wzType);
1251 ReleaseStr(pLink->wzTitle);
1252 ReleaseStr(pLink->wzRel);
1253 }
1254}
1255
1256
1257/********************************************************************
1258 FreeAtomUnknownElement - releases all of the memory used by a list of unknown elements
1259
1260*********************************************************************/
1261static void FreeAtomUnknownElementList(
1262 __in_opt ATOM_UNKNOWN_ELEMENT* pUnknownElement
1263 )
1264{
1265 while (pUnknownElement)
1266 {
1267 ATOM_UNKNOWN_ELEMENT* pFree = pUnknownElement;
1268 pUnknownElement = pUnknownElement->pNext;
1269
1270 FreeAtomUnknownAttributeList(pFree->pAttributes);
1271 ReleaseStr(pFree->wzNamespace);
1272 ReleaseStr(pFree->wzElement);
1273 ReleaseStr(pFree->wzValue);
1274 MemFree(pFree);
1275 }
1276}
1277
1278
1279/********************************************************************
1280 FreeAtomUnknownAttribute - releases all of the memory used by a list of unknown attributes
1281
1282*********************************************************************/
1283static void FreeAtomUnknownAttributeList(
1284 __in_opt ATOM_UNKNOWN_ATTRIBUTE* pUnknownAttribute
1285 )
1286{
1287 while (pUnknownAttribute)
1288 {
1289 ATOM_UNKNOWN_ATTRIBUTE* pFree = pUnknownAttribute;
1290 pUnknownAttribute = pUnknownAttribute->pNext;
1291
1292 ReleaseStr(pFree->wzNamespace);
1293 ReleaseStr(pFree->wzAttribute);
1294 ReleaseStr(pFree->wzValue);
1295 MemFree(pFree);
1296 }
1297}
diff --git a/src/libs/dutil/WixToolset.DUtil/buffutil.cpp b/src/libs/dutil/WixToolset.DUtil/buffutil.cpp
new file mode 100644
index 00000000..b6d58cc0
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/buffutil.cpp
@@ -0,0 +1,529 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5
6// Exit macros
7#define BuffExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_BUFFUTIL, x, s, __VA_ARGS__)
8#define BuffExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_BUFFUTIL, x, s, __VA_ARGS__)
9#define BuffExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_BUFFUTIL, x, s, __VA_ARGS__)
10#define BuffExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_BUFFUTIL, x, s, __VA_ARGS__)
11#define BuffExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_BUFFUTIL, x, s, __VA_ARGS__)
12#define BuffExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_BUFFUTIL, x, s, __VA_ARGS__)
13#define BuffExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_BUFFUTIL, p, x, e, s, __VA_ARGS__)
14#define BuffExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_BUFFUTIL, p, x, s, __VA_ARGS__)
15#define BuffExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_BUFFUTIL, p, x, e, s, __VA_ARGS__)
16#define BuffExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_BUFFUTIL, p, x, s, __VA_ARGS__)
17#define BuffExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_BUFFUTIL, e, x, s, __VA_ARGS__)
18#define BuffExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_BUFFUTIL, g, x, s, __VA_ARGS__)
19
20
21// constants
22
23#define BUFFER_INCREMENT 128
24
25
26// helper function declarations
27
28static HRESULT EnsureBufferSize(
29 __deref_inout_bcount(cbSize) BYTE** ppbBuffer,
30 __in SIZE_T cbSize
31 );
32
33
34// functions
35
36extern "C" HRESULT BuffReadNumber(
37 __in_bcount(cbBuffer) const BYTE* pbBuffer,
38 __in SIZE_T cbBuffer,
39 __inout SIZE_T* piBuffer,
40 __out DWORD* pdw
41 )
42{
43 Assert(pbBuffer);
44 Assert(piBuffer);
45 Assert(pdw);
46
47 HRESULT hr = S_OK;
48 SIZE_T cbAvailable = 0;
49
50 // get availiable data size
51 hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable);
52 BuffExitOnRootFailure(hr, "Failed to calculate available data size.");
53
54 // verify buffer size
55 if (sizeof(DWORD) > cbAvailable)
56 {
57 hr = E_INVALIDARG;
58 BuffExitOnRootFailure(hr, "Buffer too small.");
59 }
60
61 *pdw = *(const DWORD*)(pbBuffer + *piBuffer);
62 *piBuffer += sizeof(DWORD);
63
64LExit:
65 return hr;
66}
67
68extern "C" HRESULT BuffReadNumber64(
69 __in_bcount(cbBuffer) const BYTE * pbBuffer,
70 __in SIZE_T cbBuffer,
71 __inout SIZE_T* piBuffer,
72 __out DWORD64* pdw64
73 )
74{
75 Assert(pbBuffer);
76 Assert(piBuffer);
77 Assert(pdw64);
78
79 HRESULT hr = S_OK;
80 SIZE_T cbAvailable = 0;
81
82 // get availiable data size
83 hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable);
84 BuffExitOnRootFailure(hr, "Failed to calculate available data size.");
85
86 // verify buffer size
87 if (sizeof(DWORD64) > cbAvailable)
88 {
89 hr = E_INVALIDARG;
90 BuffExitOnRootFailure(hr, "Buffer too small.");
91 }
92
93 *pdw64 = *(const DWORD64*)(pbBuffer + *piBuffer);
94 *piBuffer += sizeof(DWORD64);
95
96LExit:
97 return hr;
98}
99
100extern "C" HRESULT BuffReadPointer(
101 __in_bcount(cbBuffer) const BYTE* pbBuffer,
102 __in SIZE_T cbBuffer,
103 __inout SIZE_T* piBuffer,
104 __out DWORD_PTR* pdw64
105 )
106{
107 Assert(pbBuffer);
108 Assert(piBuffer);
109 Assert(pdw64);
110
111 HRESULT hr = S_OK;
112 SIZE_T cbAvailable = 0;
113
114 // get availiable data size
115 hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable);
116 BuffExitOnRootFailure(hr, "Failed to calculate available data size.");
117
118 // verify buffer size
119 if (sizeof(DWORD_PTR) > cbAvailable)
120 {
121 hr = E_INVALIDARG;
122 BuffExitOnRootFailure(hr, "Buffer too small.");
123 }
124
125 *pdw64 = *(const DWORD_PTR*)(pbBuffer + *piBuffer);
126 *piBuffer += sizeof(DWORD_PTR);
127
128LExit:
129 return hr;
130}
131
132extern "C" HRESULT BuffReadString(
133 __in_bcount(cbBuffer) const BYTE* pbBuffer,
134 __in SIZE_T cbBuffer,
135 __inout SIZE_T* piBuffer,
136 __deref_out_z LPWSTR* pscz
137 )
138{
139 Assert(pbBuffer);
140 Assert(piBuffer);
141 Assert(pscz);
142
143 HRESULT hr = S_OK;
144 SIZE_T cch = 0;
145 SIZE_T cb = 0;
146 SIZE_T cbAvailable = 0;
147
148 // get availiable data size
149 hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable);
150 BuffExitOnRootFailure(hr, "Failed to calculate available data size for character count.");
151
152 // verify buffer size
153 if (sizeof(SIZE_T) > cbAvailable)
154 {
155 hr = E_INVALIDARG;
156 BuffExitOnRootFailure(hr, "Buffer too small.");
157 }
158
159 // read character count
160 cch = *(const SIZE_T*)(pbBuffer + *piBuffer);
161
162 hr = ::SIZETMult(cch, sizeof(WCHAR), &cb);
163 BuffExitOnRootFailure(hr, "Overflow while multiplying to calculate buffer size");
164
165 hr = ::SIZETAdd(*piBuffer, sizeof(SIZE_T), piBuffer);
166 BuffExitOnRootFailure(hr, "Overflow while adding to calculate buffer size");
167
168 // get availiable data size
169 hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable);
170 BuffExitOnRootFailure(hr, "Failed to calculate available data size for character buffer.");
171
172 // verify buffer size
173 if (cb > cbAvailable)
174 {
175 hr = E_INVALIDARG;
176 BuffExitOnRootFailure(hr, "Buffer too small to hold character data.");
177 }
178
179 // copy character data
180 hr = StrAllocString(pscz, cch ? (LPCWSTR)(pbBuffer + *piBuffer) : L"", cch);
181 BuffExitOnFailure(hr, "Failed to copy character data.");
182
183 *piBuffer += cb;
184
185LExit:
186 return hr;
187}
188
189extern "C" HRESULT BuffReadStringAnsi(
190 __in_bcount(cbBuffer) const BYTE* pbBuffer,
191 __in SIZE_T cbBuffer,
192 __inout SIZE_T* piBuffer,
193 __deref_out_z LPSTR* pscz
194 )
195{
196 Assert(pbBuffer);
197 Assert(piBuffer);
198 Assert(pscz);
199
200 HRESULT hr = S_OK;
201 SIZE_T cch = 0;
202 SIZE_T cb = 0;
203 SIZE_T cbAvailable = 0;
204
205 // get availiable data size
206 hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable);
207 BuffExitOnRootFailure(hr, "Failed to calculate available data size for character count.");
208
209 // verify buffer size
210 if (sizeof(SIZE_T) > cbAvailable)
211 {
212 hr = E_INVALIDARG;
213 BuffExitOnRootFailure(hr, "Buffer too small.");
214 }
215
216 // read character count
217 cch = *(const SIZE_T*)(pbBuffer + *piBuffer);
218
219 hr = ::SIZETMult(cch, sizeof(CHAR), &cb);
220 BuffExitOnRootFailure(hr, "Overflow while multiplying to calculate buffer size");
221
222 hr = ::SIZETAdd(*piBuffer, sizeof(SIZE_T), piBuffer);
223 BuffExitOnRootFailure(hr, "Overflow while adding to calculate buffer size");
224
225 // get availiable data size
226 hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable);
227 BuffExitOnRootFailure(hr, "Failed to calculate available data size for character buffer.");
228
229 // verify buffer size
230 if (cb > cbAvailable)
231 {
232 hr = E_INVALIDARG;
233 BuffExitOnRootFailure(hr, "Buffer too small to hold character count.");
234 }
235
236 // copy character data
237 hr = StrAnsiAllocStringAnsi(pscz, cch ? (LPCSTR)(pbBuffer + *piBuffer) : "", cch);
238 BuffExitOnFailure(hr, "Failed to copy character data.");
239
240 *piBuffer += cb;
241
242LExit:
243 return hr;
244}
245
246extern "C" HRESULT BuffReadStream(
247 __in_bcount(cbBuffer) const BYTE* pbBuffer,
248 __in SIZE_T cbBuffer,
249 __inout SIZE_T* piBuffer,
250 __deref_inout_bcount(*pcbStream) BYTE** ppbStream,
251 __out SIZE_T* pcbStream
252 )
253{
254 Assert(pbBuffer);
255 Assert(piBuffer);
256 Assert(ppbStream);
257 Assert(pcbStream);
258
259 HRESULT hr = S_OK;
260 SIZE_T cb = 0;
261 SIZE_T cbAvailable = 0;
262 errno_t err = 0;
263
264 // get availiable data size
265 hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable);
266 BuffExitOnRootFailure(hr, "Failed to calculate available data size for stream size.");
267
268 // verify buffer size
269 if (sizeof(SIZE_T) > cbAvailable)
270 {
271 hr = E_INVALIDARG;
272 BuffExitOnRootFailure(hr, "Buffer too small.");
273 }
274
275 // read stream size
276 cb = *(const SIZE_T*)(pbBuffer + *piBuffer);
277 *piBuffer += sizeof(SIZE_T);
278
279 // get availiable data size
280 hr = ::SIZETSub(cbBuffer, *piBuffer, &cbAvailable);
281 BuffExitOnRootFailure(hr, "Failed to calculate available data size for stream buffer.");
282
283 // verify buffer size
284 if (cb > cbAvailable)
285 {
286 hr = E_INVALIDARG;
287 BuffExitOnRootFailure(hr, "Buffer too small to hold byte count.");
288 }
289
290 // allocate buffer
291 *ppbStream = (BYTE*)MemAlloc(cb, TRUE);
292 BuffExitOnNull(*ppbStream, hr, E_OUTOFMEMORY, "Failed to allocate stream.");
293
294 // read stream data
295 err = memcpy_s(*ppbStream, cbBuffer - *piBuffer, pbBuffer + *piBuffer, cb);
296 if (err)
297 {
298 BuffExitOnRootFailure(hr = E_INVALIDARG, "Failed to read stream from buffer, error: %d", err);
299 }
300
301 *piBuffer += cb;
302
303 // return stream size
304 *pcbStream = cb;
305
306LExit:
307 return hr;
308}
309
310extern "C" HRESULT BuffWriteNumber(
311 __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer,
312 __inout SIZE_T* piBuffer,
313 __in DWORD dw
314 )
315{
316 Assert(ppbBuffer);
317 Assert(piBuffer);
318
319 HRESULT hr = S_OK;
320
321 // make sure we have a buffer with sufficient space
322 hr = EnsureBufferSize(ppbBuffer, *piBuffer + sizeof(DWORD));
323 BuffExitOnFailure(hr, "Failed to ensure buffer size.");
324
325 // copy data to buffer
326 *(DWORD*)(*ppbBuffer + *piBuffer) = dw;
327 *piBuffer += sizeof(DWORD);
328
329LExit:
330 return hr;
331}
332
333extern "C" HRESULT BuffWriteNumber64(
334 __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer,
335 __inout SIZE_T* piBuffer,
336 __in DWORD64 dw64
337 )
338{
339 Assert(ppbBuffer);
340 Assert(piBuffer);
341
342 HRESULT hr = S_OK;
343
344 // make sure we have a buffer with sufficient space
345 hr = EnsureBufferSize(ppbBuffer, *piBuffer + sizeof(DWORD64));
346 BuffExitOnFailure(hr, "Failed to ensure buffer size.");
347
348 // copy data to buffer
349 *(DWORD64*)(*ppbBuffer + *piBuffer) = dw64;
350 *piBuffer += sizeof(DWORD64);
351
352LExit:
353 return hr;
354}
355
356extern "C" HRESULT BuffWritePointer(
357 __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer,
358 __inout SIZE_T* piBuffer,
359 __in DWORD_PTR dw
360 )
361{
362 Assert(ppbBuffer);
363 Assert(piBuffer);
364
365 HRESULT hr = S_OK;
366
367 // make sure we have a buffer with sufficient space
368 hr = EnsureBufferSize(ppbBuffer, *piBuffer + sizeof(DWORD_PTR));
369 BuffExitOnFailure(hr, "Failed to ensure buffer size.");
370
371 // copy data to buffer
372 *(DWORD_PTR*)(*ppbBuffer + *piBuffer) = dw;
373 *piBuffer += sizeof(DWORD_PTR);
374
375LExit:
376 return hr;
377}
378
379extern "C" HRESULT BuffWriteString(
380 __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer,
381 __inout SIZE_T* piBuffer,
382 __in_z_opt LPCWSTR scz
383 )
384{
385 Assert(ppbBuffer);
386 Assert(piBuffer);
387
388 HRESULT hr = S_OK;
389 SIZE_T cch = 0;
390 SIZE_T cb = 0;
391 errno_t err = 0;
392
393 if (scz)
394 {
395 hr = ::StringCchLengthW(scz, STRSAFE_MAX_CCH, reinterpret_cast<size_t*>(&cch));
396 BuffExitOnRootFailure(hr, "Failed to get string size.")
397 }
398
399 cb = cch * sizeof(WCHAR);
400
401 // make sure we have a buffer with sufficient space
402 hr = EnsureBufferSize(ppbBuffer, *piBuffer + (sizeof(SIZE_T) + cb));
403 BuffExitOnFailure(hr, "Failed to ensure buffer size.");
404
405 // copy character count to buffer
406 *(SIZE_T*)(*ppbBuffer + *piBuffer) = cch;
407 *piBuffer += sizeof(SIZE_T);
408
409 // copy data to buffer
410 err = memcpy_s(*ppbBuffer + *piBuffer, cb, scz, cb);
411 if (err)
412 {
413 BuffExitOnRootFailure(hr = E_INVALIDARG, "Failed to write string to buffer: '%ls', error: %d", scz, err);
414 }
415
416 *piBuffer += cb;
417
418LExit:
419 return hr;
420}
421
422extern "C" HRESULT BuffWriteStringAnsi(
423 __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer,
424 __inout SIZE_T* piBuffer,
425 __in_z_opt LPCSTR scz
426 )
427{
428 Assert(ppbBuffer);
429 Assert(piBuffer);
430
431 HRESULT hr = S_OK;
432 SIZE_T cch = 0;
433 SIZE_T cb = 0;
434 errno_t err = 0;
435
436 if (scz)
437 {
438 hr = ::StringCchLengthA(scz, STRSAFE_MAX_CCH, reinterpret_cast<size_t*>(&cch));
439 BuffExitOnRootFailure(hr, "Failed to get string size.")
440 }
441
442 cb = cch * sizeof(CHAR);
443
444 // make sure we have a buffer with sufficient space
445 hr = EnsureBufferSize(ppbBuffer, *piBuffer + (sizeof(SIZE_T) + cb));
446 BuffExitOnFailure(hr, "Failed to ensure buffer size.");
447
448 // copy character count to buffer
449 *(SIZE_T*)(*ppbBuffer + *piBuffer) = cch;
450 *piBuffer += sizeof(SIZE_T);
451
452 // copy data to buffer
453 err = memcpy_s(*ppbBuffer + *piBuffer, cb, scz, cb);
454 if (err)
455 {
456 BuffExitOnRootFailure(hr = E_INVALIDARG, "Failed to write string to buffer: '%hs', error: %d", scz, err);
457 }
458
459 *piBuffer += cb;
460
461LExit:
462 return hr;
463}
464
465extern "C" HRESULT BuffWriteStream(
466 __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer,
467 __inout SIZE_T* piBuffer,
468 __in_bcount(cbStream) const BYTE* pbStream,
469 __in SIZE_T cbStream
470 )
471{
472 Assert(ppbBuffer);
473 Assert(piBuffer);
474 Assert(pbStream);
475
476 HRESULT hr = S_OK;
477 SIZE_T cb = cbStream;
478 errno_t err = 0;
479
480 // make sure we have a buffer with sufficient space
481 hr = EnsureBufferSize(ppbBuffer, *piBuffer + cbStream + sizeof(SIZE_T));
482 BuffExitOnFailure(hr, "Failed to ensure buffer size.");
483
484 // copy byte count to buffer
485 *(SIZE_T*)(*ppbBuffer + *piBuffer) = cb;
486 *piBuffer += sizeof(SIZE_T);
487
488 // copy data to buffer
489 err = memcpy_s(*ppbBuffer + *piBuffer, cbStream, pbStream, cbStream);
490 if (err)
491 {
492 BuffExitOnRootFailure(hr = E_INVALIDARG, "Failed to write stream to buffer, error: %d", err);
493 }
494
495 *piBuffer += cbStream;
496
497LExit:
498 return hr;
499}
500
501
502// helper functions
503
504static HRESULT EnsureBufferSize(
505 __deref_inout_bcount(cbSize) BYTE** ppbBuffer,
506 __in SIZE_T cbSize
507 )
508{
509 HRESULT hr = S_OK;
510 SIZE_T cbTarget = ((cbSize / BUFFER_INCREMENT) + 1) * BUFFER_INCREMENT;
511
512 if (*ppbBuffer)
513 {
514 if (MemSize(*ppbBuffer) < cbTarget)
515 {
516 LPVOID pv = MemReAlloc(*ppbBuffer, cbTarget, TRUE);
517 BuffExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to reallocate buffer.");
518 *ppbBuffer = (BYTE*)pv;
519 }
520 }
521 else
522 {
523 *ppbBuffer = (BYTE*)MemAlloc(cbTarget, TRUE);
524 BuffExitOnNull(*ppbBuffer, hr, E_OUTOFMEMORY, "Failed to allocate buffer.");
525 }
526
527LExit:
528 return hr;
529}
diff --git a/src/libs/dutil/WixToolset.DUtil/build/WixToolset.DUtil.props b/src/libs/dutil/WixToolset.DUtil/build/WixToolset.DUtil.props
new file mode 100644
index 00000000..35749be8
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/build/WixToolset.DUtil.props
@@ -0,0 +1,28 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3
4<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
5 <ItemDefinitionGroup>
6 <ClCompile>
7 <AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)native\include\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
8 </ClCompile>
9 <ResourceCompile>
10 <AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)native\include\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
11 </ResourceCompile>
12 </ItemDefinitionGroup>
13 <ItemDefinitionGroup Condition=" $(PlatformToolset.ToLower().StartsWith('v140')) ">
14 <Link>
15 <AdditionalDependencies>$(MSBuildThisFileDirectory)native\v140\$(PlatformTarget)\dutil.lib;%(AdditionalDependencies)</AdditionalDependencies>
16 </Link>
17 </ItemDefinitionGroup>
18 <ItemDefinitionGroup Condition=" $(PlatformToolset.ToLower().StartsWith('v141')) ">
19 <Link>
20 <AdditionalDependencies>$(MSBuildThisFileDirectory)native\v141\$(PlatformTarget)\dutil.lib;%(AdditionalDependencies)</AdditionalDependencies>
21 </Link>
22 </ItemDefinitionGroup>
23 <ItemDefinitionGroup Condition=" $(PlatformToolset.ToLower().StartsWith('v142')) ">
24 <Link>
25 <AdditionalDependencies>$(MSBuildThisFileDirectory)native\v142\$(PlatformTarget)\dutil.lib;%(AdditionalDependencies)</AdditionalDependencies>
26 </Link>
27 </ItemDefinitionGroup>
28</Project>
diff --git a/src/libs/dutil/WixToolset.DUtil/butil.cpp b/src/libs/dutil/WixToolset.DUtil/butil.cpp
new file mode 100644
index 00000000..e04b52e9
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/butil.cpp
@@ -0,0 +1,257 @@
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// Exit macros
6#define ButilExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_BUTIL, x, s, __VA_ARGS__)
7#define ButilExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_BUTIL, x, s, __VA_ARGS__)
8#define ButilExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_BUTIL, x, s, __VA_ARGS__)
9#define ButilExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_BUTIL, x, s, __VA_ARGS__)
10#define ButilExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_BUTIL, x, s, __VA_ARGS__)
11#define ButilExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_BUTIL, x, s, __VA_ARGS__)
12#define ButilExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_BUTIL, p, x, e, s, __VA_ARGS__)
13#define ButilExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_BUTIL, p, x, s, __VA_ARGS__)
14#define ButilExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_BUTIL, p, x, e, s, __VA_ARGS__)
15#define ButilExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_BUTIL, p, x, s, __VA_ARGS__)
16#define ButilExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_BUTIL, e, x, s, __VA_ARGS__)
17
18// constants
19// From engine/registration.h
20const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
21const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE = L"BundleUpgradeCode";
22const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY = L"BundleProviderKey";
23
24// Forward declarations.
25/********************************************************************
26OpenBundleKey - Opens the bundle uninstallation key for a given bundle
27
28NOTE: caller is responsible for closing key
29********************************************************************/
30static HRESULT OpenBundleKey(
31 __in_z LPCWSTR wzBundleId,
32 __in BUNDLE_INSTALL_CONTEXT context,
33 __inout HKEY *key);
34
35
36extern "C" HRESULT DAPI BundleGetBundleInfo(
37 __in_z LPCWSTR wzBundleId,
38 __in_z LPCWSTR wzAttribute,
39 __out_ecount_opt(*pcchValueBuf) LPWSTR lpValueBuf,
40 __inout_opt LPDWORD pcchValueBuf
41 )
42{
43 Assert(wzBundleId && wzAttribute);
44
45 HRESULT hr = S_OK;
46 BUNDLE_INSTALL_CONTEXT context = BUNDLE_INSTALL_CONTEXT_MACHINE;
47 LPWSTR sczValue = NULL;
48 HKEY hkBundle = NULL;
49 DWORD cchSource = 0;
50 DWORD dwType = 0;
51 DWORD dwValue = 0;
52
53 if ((lpValueBuf && !pcchValueBuf) || !wzBundleId || !wzAttribute)
54 {
55 ButilExitOnFailure(hr = E_INVALIDARG, "An invalid parameter was passed to the function.");
56 }
57
58 if (FAILED(hr = OpenBundleKey(wzBundleId, context = BUNDLE_INSTALL_CONTEXT_MACHINE, &hkBundle)) &&
59 FAILED(hr = OpenBundleKey(wzBundleId, context = BUNDLE_INSTALL_CONTEXT_USER, &hkBundle)))
60 {
61 ButilExitOnFailure(E_FILENOTFOUND == hr ? HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) : hr, "Failed to locate bundle uninstall key path.");
62 }
63
64 // If the bundle doesn't have the property defined, return ERROR_UNKNOWN_PROPERTY
65 hr = RegGetType(hkBundle, wzAttribute, &dwType);
66 ButilExitOnFailure(E_FILENOTFOUND == hr ? HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) : hr, "Failed to locate bundle property.");
67
68 switch (dwType)
69 {
70 case REG_SZ:
71 hr = RegReadString(hkBundle, wzAttribute, &sczValue);
72 ButilExitOnFailure(hr, "Failed to read string property.");
73 break;
74 case REG_DWORD:
75 hr = RegReadNumber(hkBundle, wzAttribute, &dwValue);
76 ButilExitOnFailure(hr, "Failed to read dword property.");
77
78 hr = StrAllocFormatted(&sczValue, L"%d", dwValue);
79 ButilExitOnFailure(hr, "Failed to format dword property as string.");
80 break;
81 default:
82 ButilExitOnFailure(hr = E_NOTIMPL, "Reading bundle info of type 0x%x not implemented.", dwType);
83
84 }
85
86 hr = ::StringCchLengthW(sczValue, STRSAFE_MAX_CCH, reinterpret_cast<UINT_PTR*>(&cchSource));
87 ButilExitOnFailure(hr, "Failed to calculate length of string");
88
89 if (lpValueBuf)
90 {
91 // cchSource is the length of the string not including the terminating null character
92 if (*pcchValueBuf <= cchSource)
93 {
94 *pcchValueBuf = ++cchSource;
95 ButilExitOnFailure(hr = HRESULT_FROM_WIN32(ERROR_MORE_DATA), "A buffer is too small to hold the requested data.");
96 }
97
98 hr = ::StringCchCatNExW(lpValueBuf, *pcchValueBuf, sczValue, cchSource, NULL, NULL, STRSAFE_FILL_BEHIND_NULL);
99 ButilExitOnFailure(hr, "Failed to copy the property value to the output buffer.");
100
101 *pcchValueBuf = cchSource++;
102 }
103
104LExit:
105 ReleaseRegKey(hkBundle);
106 ReleaseStr(sczValue);
107
108 return hr;
109}
110
111HRESULT DAPI BundleEnumRelatedBundle(
112 __in_z LPCWSTR wzUpgradeCode,
113 __in BUNDLE_INSTALL_CONTEXT context,
114 __inout PDWORD pdwStartIndex,
115 __out_ecount(MAX_GUID_CHARS+1) LPWSTR lpBundleIdBuf
116 )
117{
118 HRESULT hr = S_OK;
119 HKEY hkRoot = BUNDLE_INSTALL_CONTEXT_USER == context ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE;
120 HKEY hkUninstall = NULL;
121 HKEY hkBundle = NULL;
122 LPWSTR sczUninstallSubKey = NULL;
123 DWORD cchUninstallSubKey = 0;
124 LPWSTR sczUninstallSubKeyPath = NULL;
125 LPWSTR sczValue = NULL;
126 DWORD dwType = 0;
127
128 LPWSTR* rgsczBundleUpgradeCodes = NULL;
129 DWORD cBundleUpgradeCodes = 0;
130 BOOL fUpgradeCodeFound = FALSE;
131
132 if (!wzUpgradeCode || !lpBundleIdBuf || !pdwStartIndex)
133 {
134 ButilExitOnFailure(hr = E_INVALIDARG, "An invalid parameter was passed to the function.");
135 }
136
137 hr = RegOpen(hkRoot, BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY, KEY_READ, &hkUninstall);
138 ButilExitOnFailure(hr, "Failed to open bundle uninstall key path.");
139
140 for (DWORD dwIndex = *pdwStartIndex; !fUpgradeCodeFound; dwIndex++)
141 {
142 hr = RegKeyEnum(hkUninstall, dwIndex, &sczUninstallSubKey);
143 ButilExitOnFailure(hr, "Failed to enumerate bundle uninstall key path.");
144
145 hr = StrAllocFormatted(&sczUninstallSubKeyPath, L"%ls\\%ls", BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY, sczUninstallSubKey);
146 ButilExitOnFailure(hr, "Failed to allocate bundle uninstall key path.");
147
148 hr = RegOpen(hkRoot, sczUninstallSubKeyPath, KEY_READ, &hkBundle);
149 ButilExitOnFailure(hr, "Failed to open uninstall key path.");
150
151 // If it's a bundle, it should have a BundleUpgradeCode value of type REG_SZ (old) or REG_MULTI_SZ
152 hr = RegGetType(hkBundle, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &dwType);
153 if (FAILED(hr))
154 {
155 ReleaseRegKey(hkBundle);
156 ReleaseNullStr(sczUninstallSubKey);
157 ReleaseNullStr(sczUninstallSubKeyPath);
158 // Not a bundle
159 continue;
160 }
161
162 switch (dwType)
163 {
164 case REG_SZ:
165 hr = RegReadString(hkBundle, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &sczValue);
166 ButilExitOnFailure(hr, "Failed to read BundleUpgradeCode string property.");
167 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, sczValue, -1, wzUpgradeCode, -1))
168 {
169 *pdwStartIndex = dwIndex;
170 fUpgradeCodeFound = TRUE;
171 break;
172 }
173
174 ReleaseNullStr(sczValue);
175
176 break;
177 case REG_MULTI_SZ:
178 hr = RegReadStringArray(hkBundle, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &rgsczBundleUpgradeCodes, &cBundleUpgradeCodes);
179 ButilExitOnFailure(hr, "Failed to read BundleUpgradeCode multi-string property.");
180
181 for (DWORD i = 0; i < cBundleUpgradeCodes; i++)
182 {
183 LPWSTR wzBundleUpgradeCode = rgsczBundleUpgradeCodes[i];
184 if (wzBundleUpgradeCode && *wzBundleUpgradeCode)
185 {
186 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, wzBundleUpgradeCode, -1, wzUpgradeCode, -1))
187 {
188 *pdwStartIndex = dwIndex;
189 fUpgradeCodeFound = TRUE;
190 break;
191 }
192 }
193 }
194 ReleaseNullStrArray(rgsczBundleUpgradeCodes, cBundleUpgradeCodes);
195
196 break;
197
198 default:
199 ButilExitOnFailure(hr = E_NOTIMPL, "BundleUpgradeCode of type 0x%x not implemented.", dwType);
200
201 }
202
203 if (fUpgradeCodeFound)
204 {
205 if (lpBundleIdBuf)
206 {
207 hr = ::StringCchLengthW(sczUninstallSubKey, STRSAFE_MAX_CCH, reinterpret_cast<UINT_PTR*>(&cchUninstallSubKey));
208 ButilExitOnFailure(hr, "Failed to calculate length of string");
209
210 hr = ::StringCchCopyNExW(lpBundleIdBuf, MAX_GUID_CHARS + 1, sczUninstallSubKey, cchUninstallSubKey, NULL, NULL, STRSAFE_FILL_BEHIND_NULL);
211 ButilExitOnFailure(hr, "Failed to copy the property value to the output buffer.");
212 }
213
214 break;
215 }
216
217 // Cleanup before next iteration
218 ReleaseRegKey(hkBundle);
219 ReleaseNullStr(sczUninstallSubKey);
220 ReleaseNullStr(sczUninstallSubKeyPath);
221 }
222
223LExit:
224 ReleaseStr(sczValue);
225 ReleaseStr(sczUninstallSubKey);
226 ReleaseStr(sczUninstallSubKeyPath);
227 ReleaseRegKey(hkBundle);
228 ReleaseRegKey(hkUninstall);
229 ReleaseStrArray(rgsczBundleUpgradeCodes, cBundleUpgradeCodes);
230
231 return hr;
232}
233
234
235HRESULT OpenBundleKey(
236 __in_z LPCWSTR wzBundleId,
237 __in BUNDLE_INSTALL_CONTEXT context,
238 __inout HKEY *key)
239{
240 Assert(key && wzBundleId);
241 AssertSz(NULL == *key, "*key should be null");
242
243 HRESULT hr = S_OK;
244 HKEY hkRoot = BUNDLE_INSTALL_CONTEXT_USER == context ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE;
245 LPWSTR sczKeypath = NULL;
246
247 hr = StrAllocFormatted(&sczKeypath, L"%ls\\%ls", BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY, wzBundleId);
248 ButilExitOnFailure(hr, "Failed to allocate bundle uninstall key path.");
249
250 hr = RegOpen(hkRoot, sczKeypath, KEY_READ, key);
251 ButilExitOnFailure(hr, "Failed to open bundle uninstall key path.");
252
253LExit:
254 ReleaseStr(sczKeypath);
255
256 return hr;
257}
diff --git a/src/libs/dutil/WixToolset.DUtil/cabcutil.cpp b/src/libs/dutil/WixToolset.DUtil/cabcutil.cpp
new file mode 100644
index 00000000..93a9b7e1
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/cabcutil.cpp
@@ -0,0 +1,1539 @@
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// Exit macros
7#define CabcExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_CABCUTIL, x, s, __VA_ARGS__)
8#define CabcExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_CABCUTIL, x, s, __VA_ARGS__)
9#define CabcExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_CABCUTIL, x, s, __VA_ARGS__)
10#define CabcExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_CABCUTIL, x, s, __VA_ARGS__)
11#define CabcExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_CABCUTIL, x, s, __VA_ARGS__)
12#define CabcExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_CABCUTIL, x, s, __VA_ARGS__)
13#define CabcExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_CABCUTIL, p, x, e, s, __VA_ARGS__)
14#define CabcExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_CABCUTIL, p, x, s, __VA_ARGS__)
15#define CabcExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_CABCUTIL, p, x, e, s, __VA_ARGS__)
16#define CabcExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_CABCUTIL, p, x, s, __VA_ARGS__)
17#define CabcExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_CABCUTIL, e, x, s, __VA_ARGS__)
18#define CabcExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_CABCUTIL, g, x, s, __VA_ARGS__)
19
20
21static const WCHAR CABC_MAGIC_UNICODE_STRING_MARKER = '?';
22static const DWORD MAX_CABINET_HEADER_SIZE = 16 * 1024 * 1024;
23
24// The minimum number of uncompressed bytes between FciFlushFolder() calls - if we call FciFlushFolder()
25// too often (because of duplicates too close together) we theoretically ruin our compression ratio -
26// left at zero to maximize install-time performance, because even a small minimum threshhold seems to
27// have a high install-time performance cost for little or no size benefit. The value is left here for
28// tweaking though - possible suggested values are 524288 for 512K, or 2097152 for 2MB.
29static const DWORD MINFLUSHTHRESHHOLD = 0;
30
31// structs
32struct MS_CABINET_HEADER
33{
34 DWORD sig;
35 DWORD csumHeader;
36 DWORD cbCabinet;
37 DWORD csumFolders;
38 DWORD coffFiles;
39 DWORD csumFiles;
40 WORD version;
41 WORD cFolders;
42 WORD cFiles;
43 WORD flags;
44 WORD setID;
45 WORD iCabinet;
46};
47
48
49struct MS_CABINET_ITEM
50{
51 DWORD cbFile;
52 DWORD uoffFolderStart;
53 WORD iFolder;
54 WORD date;
55 WORD time;
56 WORD attribs;
57};
58
59struct CABC_INTERNAL_ADDFILEINFO
60{
61 LPCWSTR wzSourcePath;
62 LPCWSTR wzEmptyPath;
63};
64
65struct CABC_DUPLICATEFILE
66{
67 DWORD dwFileArrayIndex;
68 DWORD dwDuplicateCabFileIndex;
69 LPWSTR pwzSourcePath;
70 LPWSTR pwzToken;
71};
72
73
74struct CABC_FILE
75{
76 DWORD dwCabFileIndex;
77 LPWSTR pwzSourcePath;
78 LPWSTR pwzToken;
79 PMSIFILEHASHINFO pmfHash;
80 LONGLONG llFileSize;
81 BOOL fHasDuplicates;
82};
83
84
85struct CABC_DATA
86{
87 LONGLONG llBytesSinceLastFlush;
88 LONGLONG llFlushThreshhold;
89
90 STRINGDICT_HANDLE shDictHandle;
91
92 WCHAR wzCabinetPath[MAX_PATH];
93 WCHAR wzEmptyFile[MAX_PATH];
94 HANDLE hEmptyFile;
95 DWORD dwLastFileIndex;
96
97 DWORD cFilePaths;
98 DWORD cMaxFilePaths;
99 CABC_FILE *prgFiles;
100
101 DWORD cDuplicates;
102 DWORD cMaxDuplicates;
103 CABC_DUPLICATEFILE *prgDuplicates;
104
105 HRESULT hrLastError;
106 BOOL fGoodCab;
107
108 HFCI hfci;
109 ERF erf;
110 CCAB ccab;
111 TCOMP tc;
112
113 // Below Field are used for Cabinet Splitting
114 BOOL fCabinetSplittingEnabled;
115 FileSplitCabNamesCallback fileSplitCabNamesCallback;
116 WCHAR wzFirstCabinetName[MAX_PATH]; // Stores Name of First Cabinet excluding ".cab" extention to help generate other names by Splitting
117};
118
119const int CABC_HANDLE_BYTES = sizeof(CABC_DATA);
120
121//
122// prototypes
123//
124static void FreeCabCData(
125 __in CABC_DATA* pcd
126 );
127static HRESULT CheckForDuplicateFile(
128 __in CABC_DATA *pcd,
129 __out CABC_FILE **ppcf,
130 __in LPCWSTR wzFileName,
131 __in PMSIFILEHASHINFO *ppmfHash,
132 __in LONGLONG llFileSize
133 );
134static HRESULT AddDuplicateFile(
135 __in CABC_DATA *pcd,
136 __in DWORD dwFileArrayIndex,
137 __in_z LPCWSTR wzSourcePath,
138 __in_opt LPCWSTR wzToken,
139 __in DWORD dwDuplicateCabFileIndex
140 );
141static HRESULT AddNonDuplicateFile(
142 __in CABC_DATA *pcd,
143 __in LPCWSTR wzFile,
144 __in_opt LPCWSTR wzToken,
145 __in_opt const MSIFILEHASHINFO* pmfHash,
146 __in LONGLONG llFileSize,
147 __in DWORD dwCabFileIndex
148 );
149static HRESULT UpdateDuplicateFiles(
150 __in const CABC_DATA *pcd
151 );
152static HRESULT DuplicateFile(
153 __in MS_CABINET_HEADER *pHeader,
154 __in const CABC_DATA *pcd,
155 __in const CABC_DUPLICATEFILE *pDuplicate
156 );
157static HRESULT UtcFileTimeToLocalDosDateTime(
158 __in const FILETIME* pFileTime,
159 __out USHORT* pDate,
160 __out USHORT* pTime
161 );
162
163static __callback int DIAMONDAPI CabCFilePlaced(__in PCCAB pccab, __in_z PSTR szFile, __in long cbFile, __in BOOL fContinuation, __inout_bcount(CABC_HANDLE_BYTES) void *pv);
164static __callback void * DIAMONDAPI CabCAlloc(__in ULONG cb);
165static __callback void DIAMONDAPI CabCFree(__out_bcount(CABC_HANDLE_BYTES) void *pv);
166static __callback INT_PTR DIAMONDAPI CabCOpen(__in_z PSTR pszFile, __in int oflag, __in int pmode, __out int *err, __inout_bcount(CABC_HANDLE_BYTES) void *pv);
167static __callback UINT FAR DIAMONDAPI CabCRead(__in INT_PTR hf, __out_bcount(cb) void FAR *memory, __in UINT cb, __out int *err, __inout_bcount(CABC_HANDLE_BYTES) void *pv);
168static __callback UINT FAR DIAMONDAPI CabCWrite(__in INT_PTR hf, __in_bcount(cb) void FAR *memory, __in UINT cb, __out int *err, __inout_bcount(CABC_HANDLE_BYTES) void *pv);
169static __callback long FAR DIAMONDAPI CabCSeek(__in INT_PTR hf, __in long dist, __in int seektype, __out int *err, __inout_bcount(CABC_HANDLE_BYTES) void *pv);
170static __callback int FAR DIAMONDAPI CabCClose(__in INT_PTR hf, __out int *err, __inout_bcount(CABC_HANDLE_BYTES) void *pv);
171static __callback int DIAMONDAPI CabCDelete(__in_z PSTR szFile, __out int *err, __inout_bcount(CABC_HANDLE_BYTES) void *pv);
172__success(return != FALSE) static __callback BOOL DIAMONDAPI CabCGetTempFile(__out_bcount_z(cbFile) char *szFile, __in int cbFile, __inout_bcount(CABC_HANDLE_BYTES) void *pv);
173__success(return != FALSE) static __callback BOOL DIAMONDAPI CabCGetNextCabinet(__in PCCAB pccab, __in ULONG ul, __out_bcount(CABC_HANDLE_BYTES) void *pv);
174static __callback INT_PTR DIAMONDAPI CabCGetOpenInfo(__in_z PSTR pszName, __out USHORT *pdate, __out USHORT *ptime, __out USHORT *pattribs, __out int *err, __out_bcount(CABC_HANDLE_BYTES) void *pv);
175static __callback long DIAMONDAPI CabCStatus(__in UINT uiTypeStatus, __in ULONG cb1, __in ULONG cb2, __inout_bcount(CABC_HANDLE_BYTES) void *pv);
176
177
178/********************************************************************
179CabcBegin - begins creating a cabinet
180
181NOTE: phContext must be the same handle used in AddFile and Finish.
182 wzCabDir can be L"", but not NULL.
183 dwMaxSize and dwMaxThresh can be 0. A large default value will be used in that case.
184
185********************************************************************/
186extern "C" HRESULT DAPI CabCBegin(
187 __in_z LPCWSTR wzCab,
188 __in_z LPCWSTR wzCabDir,
189 __in DWORD dwMaxFiles,
190 __in DWORD dwMaxSize,
191 __in DWORD dwMaxThresh,
192 __in COMPRESSION_TYPE ct,
193 __out_bcount(CABC_HANDLE_BYTES) HANDLE *phContext
194 )
195{
196 Assert(wzCab && *wzCab && phContext);
197
198 HRESULT hr = S_OK;
199 CABC_DATA *pcd = NULL;
200 WCHAR wzTempPath[MAX_PATH] = { };
201
202 C_ASSERT(sizeof(MSIFILEHASHINFO) == 20);
203
204 WCHAR wzPathBuffer [MAX_PATH] = L"";
205 size_t cchPathBuffer;
206 if (wzCabDir)
207 {
208 hr = ::StringCchLengthW(wzCabDir, MAX_PATH, &cchPathBuffer);
209 CabcExitOnFailure(hr, "Failed to get length of cab directory");
210
211 // Need room to terminate with L'\\' and L'\0'
212 if((MAX_PATH - 1) <= cchPathBuffer || 0 == cchPathBuffer)
213 {
214 hr = E_INVALIDARG;
215 CabcExitOnFailure(hr, "Cab directory had invalid length: %u", cchPathBuffer);
216 }
217
218 hr = ::StringCchCopyW(wzPathBuffer, countof(wzPathBuffer), wzCabDir);
219 CabcExitOnFailure(hr, "Failed to copy cab directory to buffer");
220
221 if (L'\\' != wzPathBuffer[cchPathBuffer - 1])
222 {
223 hr = ::StringCchCatW(wzPathBuffer, countof(wzPathBuffer), L"\\");
224 CabcExitOnFailure(hr, "Failed to cat \\ to end of buffer");
225 ++cchPathBuffer;
226 }
227 }
228
229 pcd = static_cast<CABC_DATA*>(MemAlloc(sizeof(CABC_DATA), TRUE));
230 CabcExitOnNull(pcd, hr, E_OUTOFMEMORY, "failed to allocate cab creation data structure");
231
232 pcd->hrLastError = S_OK;
233 pcd->fGoodCab = TRUE;
234 pcd->llFlushThreshhold = MINFLUSHTHRESHHOLD;
235
236 pcd->hEmptyFile = INVALID_HANDLE_VALUE;
237
238 pcd->fileSplitCabNamesCallback = NULL;
239
240 if (NULL == dwMaxSize)
241 {
242 pcd->ccab.cb = CAB_MAX_SIZE;
243 pcd->fCabinetSplittingEnabled = FALSE; // If no max cab size is supplied, cabinet splitting is not desired
244 }
245 else
246 {
247 pcd->ccab.cb = dwMaxSize * 1024 * 1024;
248 pcd->fCabinetSplittingEnabled = TRUE;
249 }
250
251 if (0 == dwMaxThresh)
252 {
253 // Subtract 16 to magically make cabbing of uncompressed data larger than 2GB work.
254 pcd->ccab.cbFolderThresh = CAB_MAX_SIZE - 16;
255 }
256 else
257 {
258 pcd->ccab.cbFolderThresh = dwMaxThresh;
259 }
260
261 // Translate the compression type
262 if (COMPRESSION_TYPE_NONE == ct)
263 {
264 pcd->tc = tcompTYPE_NONE;
265 }
266 else if (COMPRESSION_TYPE_LOW == ct)
267 {
268 pcd->tc = tcompTYPE_LZX | tcompLZX_WINDOW_LO;
269 }
270 else if (COMPRESSION_TYPE_MEDIUM == ct)
271 {
272 pcd->tc = TCOMPfromLZXWindow(18);
273 }
274 else if (COMPRESSION_TYPE_HIGH == ct)
275 {
276 pcd->tc = tcompTYPE_LZX | tcompLZX_WINDOW_HI;
277 }
278 else if (COMPRESSION_TYPE_MSZIP == ct)
279 {
280 pcd->tc = tcompTYPE_MSZIP;
281 }
282 else
283 {
284 hr = E_INVALIDARG;
285 CabcExitOnFailure(hr, "Invalid compression type specified.");
286 }
287
288 if (0 == ::WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, wzCab, -1, pcd->ccab.szCab, sizeof(pcd->ccab.szCab), NULL, NULL))
289 {
290 CabcExitWithLastError(hr, "failed to convert cab name to multi-byte");
291 }
292
293 if (0 == ::WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, wzPathBuffer, -1, pcd->ccab.szCabPath, sizeof(pcd->ccab.szCab), NULL, NULL))
294 {
295 CabcExitWithLastError(hr, "failed to convert cab dir to multi-byte");
296 }
297
298 // Remember the path to the cabinet.
299 hr= ::StringCchCopyW(pcd->wzCabinetPath, countof(pcd->wzCabinetPath), wzPathBuffer);
300 CabcExitOnFailure(hr, "Failed to copy cabinet path from path: %ls", wzPathBuffer);
301
302 hr = ::StringCchCatW(pcd->wzCabinetPath, countof(pcd->wzCabinetPath), wzCab);
303 CabcExitOnFailure(hr, "Failed to concat to cabinet path cabinet name: %ls", wzCab);
304
305 // Get the empty file to use as the blank marker for duplicates.
306 if (!::GetTempPathW(countof(wzTempPath), wzTempPath))
307 {
308 CabcExitWithLastError(hr, "Failed to get temp path.");
309 }
310
311 if (!::GetTempFileNameW(wzTempPath, L"WSC", 0, pcd->wzEmptyFile))
312 {
313 CabcExitWithLastError(hr, "Failed to create a temp file name.");
314 }
315
316 // Try to open the newly created empty file (remember, GetTempFileName() is kind enough to create a file for us)
317 // with a handle to automatically delete the file on close. Ignore any failure that might happen, since the worst
318 // case is we'll leave a zero byte file behind in the temp folder.
319 pcd->hEmptyFile = ::CreateFileW(pcd->wzEmptyFile, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL);
320
321 hr = DictCreateWithEmbeddedKey(&pcd->shDictHandle, dwMaxFiles, reinterpret_cast<void **>(&pcd->prgFiles), offsetof(CABC_FILE, pwzSourcePath), DICT_FLAG_CASEINSENSITIVE);
322 CabcExitOnFailure(hr, "Failed to create dictionary to keep track of duplicate files");
323
324 // Make sure to allocate at least some space, or we won't be able to realloc later if they "lied" about having zero files
325 if (1 > dwMaxFiles)
326 {
327 dwMaxFiles = 1;
328 }
329
330 pcd->cMaxFilePaths = dwMaxFiles;
331 size_t cbFileAllocSize = 0;
332
333 hr = ::SizeTMult(pcd->cMaxFilePaths, sizeof(CABC_FILE), &(cbFileAllocSize));
334 CabcExitOnFailure(hr, "Maximum allocation exceeded on initialization.");
335
336 pcd->prgFiles = static_cast<CABC_FILE*>(MemAlloc(cbFileAllocSize, TRUE));
337 CabcExitOnNull(pcd->prgFiles, hr, E_OUTOFMEMORY, "Failed to allocate memory for files.");
338
339 // Tell cabinet API about our configuration.
340 pcd->hfci = ::FCICreate(&(pcd->erf), CabCFilePlaced, CabCAlloc, CabCFree, CabCOpen, CabCRead, CabCWrite, CabCClose, CabCSeek, CabCDelete, CabCGetTempFile, &(pcd->ccab), pcd);
341 if (NULL == pcd->hfci || pcd->erf.fError)
342 {
343 // Prefer our recorded last error, then ::GetLastError(), finally fallback to the useless "E_FAIL" error
344 if (FAILED(pcd->hrLastError))
345 {
346 hr = pcd->hrLastError;
347 }
348 else
349 {
350 CabcExitWithLastError(hr, "failed to create FCI object Oper: 0x%x Type: 0x%x", pcd->erf.erfOper, pcd->erf.erfType);
351 }
352
353 pcd->fGoodCab = FALSE;
354
355 CabcExitOnFailure(hr, "failed to create FCI object Oper: 0x%x Type: 0x%x", pcd->erf.erfOper, pcd->erf.erfType); // TODO: can these be converted to HRESULTS?
356 }
357
358 *phContext = pcd;
359
360LExit:
361 if (FAILED(hr) && pcd && pcd->hfci)
362 {
363 ::FCIDestroy(pcd->hfci);
364 }
365
366 return hr;
367}
368
369
370/********************************************************************
371CabCNextCab - This will be useful when creating multiple cabs.
372Haven't needed it yet.
373********************************************************************/
374extern "C" HRESULT DAPI CabCNextCab(
375 __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext
376 )
377{
378 UNREFERENCED_PARAMETER(hContext);
379 // TODO: Make the appropriate FCIFlushCabinet and FCIFlushFolder calls
380 return E_NOTIMPL;
381}
382
383
384/********************************************************************
385CabcAddFile - adds a file to a cabinet
386
387NOTE: hContext must be the same used in Begin and Finish
388if wzToken is null, the file's original name is used within the cab
389********************************************************************/
390extern "C" HRESULT DAPI CabCAddFile(
391 __in_z LPCWSTR wzFile,
392 __in_z_opt LPCWSTR wzToken,
393 __in_opt PMSIFILEHASHINFO pmfHash,
394 __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext
395 )
396{
397 Assert(wzFile && *wzFile && hContext);
398
399 HRESULT hr = S_OK;
400 CABC_DATA *pcd = reinterpret_cast<CABC_DATA*>(hContext);
401 CABC_FILE *pcfDuplicate = NULL;
402 LONGLONG llFileSize = 0;
403 PMSIFILEHASHINFO pmfLocalHash = pmfHash;
404
405 // Use Smart Cabbing if there are duplicates and if Cabinet Splitting is not desired
406 // For Cabinet Spliting avoid hashing as Smart Cabbing is disabled
407 if(!pcd->fCabinetSplittingEnabled)
408 {
409 // Store file size, primarily used to determine which files to hash for duplicates
410 hr = FileSize(wzFile, &llFileSize);
411 CabcExitOnFailure(hr, "Failed to check size of file %ls", wzFile);
412
413 hr = CheckForDuplicateFile(pcd, &pcfDuplicate, wzFile, &pmfLocalHash, llFileSize);
414 CabcExitOnFailure(hr, "Failed while checking for duplicate of file: %ls", wzFile);
415 }
416
417 if (pcfDuplicate) // This will be null for smart cabbing case
418 {
419 DWORD index;
420 hr = ::PtrdiffTToDWord(pcfDuplicate - pcd->prgFiles, &index);
421 CabcExitOnFailure(hr, "Failed to calculate index of file name: %ls", pcfDuplicate->pwzSourcePath);
422
423 hr = AddDuplicateFile(pcd, index, wzFile, wzToken, pcd->dwLastFileIndex);
424 CabcExitOnFailure(hr, "Failed to add duplicate of file name: %ls", pcfDuplicate->pwzSourcePath);
425 }
426 else
427 {
428 hr = AddNonDuplicateFile(pcd, wzFile, wzToken, pmfLocalHash, llFileSize, pcd->dwLastFileIndex);
429 CabcExitOnFailure(hr, "Failed to add non-duplicated file: %ls", wzFile);
430 }
431
432 ++pcd->dwLastFileIndex;
433
434LExit:
435 // If we allocated a hash struct ourselves, free it
436 if (pmfHash != pmfLocalHash)
437 {
438 ReleaseMem(pmfLocalHash);
439 }
440
441 return hr;
442}
443
444
445/********************************************************************
446CabcFinish - finishes making a cabinet
447
448NOTE: hContext must be the same used in Begin and AddFile
449*********************************************************************/
450extern "C" HRESULT DAPI CabCFinish(
451 __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext,
452 __in_opt FileSplitCabNamesCallback fileSplitCabNamesCallback
453 )
454{
455 Assert(hContext);
456
457 HRESULT hr = S_OK;
458 CABC_DATA *pcd = reinterpret_cast<CABC_DATA*>(hContext);
459 CABC_INTERNAL_ADDFILEINFO fileInfo = { };
460 DWORD dwCabFileIndex; // Total file index, counts up to pcd->dwLastFileIndex
461 DWORD dwArrayFileIndex = 0; // Index into pcd->prgFiles[] array
462 DWORD dwDupeArrayFileIndex = 0; // Index into pcd->prgDuplicates[] array
463 LPSTR pszFileToken = NULL;
464 LONGLONG llFileSize = 0;
465
466 pcd->fileSplitCabNamesCallback = fileSplitCabNamesCallback;
467
468 // These are used to determine whether to call FciFlushFolder() before or after the next call to FciAddFile()
469 // doing so at appropriate times results in install-time performance benefits in the case of duplicate files.
470 // Basically, when MSI has to extract files out of order (as it does due to our smart cabbing), it can't just jump
471 // exactly to the out of order file, it must begin extracting all over again, starting from that file's CAB folder
472 // (this is not the same as a regular folder, and is a concept unique to CABs).
473
474 // This means MSI spends a lot of time extracting the same files twice, especially if the duplicate file has many files
475 // before it in the CAB folder. To avoid this, we want to make sure whenever MSI jumps to another file in the CAB, that
476 // file is at the beginning of its own folder, so no extra files need to be extracted. FciFlushFolder() causes the CAB
477 // to close the current folder, and create a new folder for the next file to be added.
478
479 // So to maximize our performance benefit, we must call FciFlushFolder() at every place MSI will jump "to" in the CAB sequence.
480 // So, we call FciFlushFolder() before adding the original version of a duplicated file (as this will be jumped "to")
481 // And we call FciFlushFolder() after adding the duplicate versions of files (as this will be jumped back "to" to get back in the regular sequence)
482 BOOL fFlushBefore = FALSE;
483 BOOL fFlushAfter = FALSE;
484
485 ReleaseDict(pcd->shDictHandle);
486
487 // We need to go through all the files, duplicates and non-duplicates, sequentially in the order they were added
488 for (dwCabFileIndex = 0; dwCabFileIndex < pcd->dwLastFileIndex; ++dwCabFileIndex)
489 {
490 if (dwArrayFileIndex < pcd->cMaxFilePaths && pcd->prgFiles[dwArrayFileIndex].dwCabFileIndex == dwCabFileIndex) // If it's a non-duplicate file
491 {
492 // Just a normal, non-duplicated file. We'll add it to the list for later checking of
493 // duplicates.
494 fileInfo.wzSourcePath = pcd->prgFiles[dwArrayFileIndex].pwzSourcePath;
495 fileInfo.wzEmptyPath = NULL;
496
497 // Use the provided token, otherwise default to the source file name.
498 if (pcd->prgFiles[dwArrayFileIndex].pwzToken)
499 {
500 LPCWSTR pwzTemp = pcd->prgFiles[dwArrayFileIndex].pwzToken;
501 hr = StrAnsiAllocString(&pszFileToken, pwzTemp, 0, CP_ACP);
502 CabcExitOnFailure(hr, "failed to convert file token to ANSI: %ls", pwzTemp);
503 }
504 else
505 {
506 LPCWSTR pwzTemp = FileFromPath(fileInfo.wzSourcePath);
507 hr = StrAnsiAllocString(&pszFileToken, pwzTemp, 0, CP_ACP);
508 CabcExitOnFailure(hr, "failed to convert file name to ANSI: %ls", pwzTemp);
509 }
510
511 if (pcd->prgFiles[dwArrayFileIndex].fHasDuplicates)
512 {
513 fFlushBefore = TRUE;
514 }
515
516 llFileSize = pcd->prgFiles[dwArrayFileIndex].llFileSize;
517
518 ++dwArrayFileIndex; // Increment into the non-duplicate array
519 }
520 else if (dwDupeArrayFileIndex < pcd->cMaxDuplicates && pcd->prgDuplicates[dwDupeArrayFileIndex].dwDuplicateCabFileIndex == dwCabFileIndex) // If it's a duplicate file
521 {
522 // For duplicate files, we point them at our empty (zero-byte) file so it takes up no space
523 // in the resultant cabinet. Later on (CabCFinish) we'll go through and change all the zero
524 // byte files to point at their duplicated file index.
525 //
526 // Notice that duplicate files are not added to the list of file paths because all duplicate
527 // files point at the same path (the empty file) so there is no point in tracking them with
528 // their path.
529 fileInfo.wzSourcePath = pcd->prgDuplicates[dwDupeArrayFileIndex].pwzSourcePath;
530 fileInfo.wzEmptyPath = pcd->wzEmptyFile;
531
532 // Use the provided token, otherwise default to the source file name.
533 if (pcd->prgDuplicates[dwDupeArrayFileIndex].pwzToken)
534 {
535 LPCWSTR pwzTemp = pcd->prgDuplicates[dwDupeArrayFileIndex].pwzToken;
536 hr = StrAnsiAllocString(&pszFileToken, pwzTemp, 0, CP_ACP);
537 CabcExitOnFailure(hr, "failed to convert duplicate file token to ANSI: %ls", pwzTemp);
538 }
539 else
540 {
541 LPCWSTR pwzTemp = FileFromPath(fileInfo.wzSourcePath);
542 hr = StrAnsiAllocString(&pszFileToken, pwzTemp, 0, CP_ACP);
543 CabcExitOnFailure(hr, "failed to convert duplicate file name to ANSI: %ls", pwzTemp);
544 }
545
546 // Flush afterward only if this isn't a duplicate of the previous file, and at least one non-duplicate file remains to be added to the cab
547 if (!(dwCabFileIndex - 1 == pcd->prgFiles[pcd->prgDuplicates[dwDupeArrayFileIndex].dwFileArrayIndex].dwCabFileIndex) &&
548 !(dwDupeArrayFileIndex > 0 && dwCabFileIndex - 1 == pcd->prgDuplicates[dwDupeArrayFileIndex - 1].dwDuplicateCabFileIndex) &&
549 dwArrayFileIndex < pcd->cFilePaths)
550 {
551 fFlushAfter = TRUE;
552 }
553
554 // We're just adding a 0-byte file, so set it appropriately
555 llFileSize = 0;
556
557 ++dwDupeArrayFileIndex; // Increment into the duplicate array
558 }
559 else // If it's neither duplicate nor non-duplicate, throw an error
560 {
561 hr = HRESULT_FROM_WIN32(ERROR_EA_LIST_INCONSISTENT);
562 CabcExitOnRootFailure(hr, "Internal inconsistency in data structures while creating CAB file - a non-standard, non-duplicate file was encountered");
563 }
564
565 if (fFlushBefore && pcd->llBytesSinceLastFlush > pcd->llFlushThreshhold)
566 {
567 if (!::FCIFlushFolder(pcd->hfci, CabCGetNextCabinet, CabCStatus))
568 {
569 CabcExitWithLastError(hr, "failed to flush FCI folder before adding file, Oper: 0x%x Type: 0x%x", pcd->erf.erfOper, pcd->erf.erfType);
570 }
571 pcd->llBytesSinceLastFlush = 0;
572 }
573
574 pcd->llBytesSinceLastFlush += llFileSize;
575
576 // Add the file to the cab. Notice that we are passing our CABC_INTERNAL_ADDFILEINFO struct
577 // through the pointer to an ANSI string. This is neccessary so we can smuggle through the
578 // path to the empty file (should this be a duplicate file).
579#pragma prefast(push)
580#pragma prefast(disable:6387) // OACR is silly, pszFileToken can't be false here
581 if (!::FCIAddFile(pcd->hfci, reinterpret_cast<LPSTR>(&fileInfo), pszFileToken, FALSE, CabCGetNextCabinet, CabCStatus, CabCGetOpenInfo, pcd->tc))
582#pragma prefast(pop)
583 {
584 pcd->fGoodCab = FALSE;
585
586 // Prefer our recorded last error, then ::GetLastError(), finally fallback to the useless "E_FAIL" error
587 if (FAILED(pcd->hrLastError))
588 {
589 hr = pcd->hrLastError;
590 }
591 else
592 {
593 CabcExitWithLastError(hr, "failed to add file to FCI object Oper: 0x%x Type: 0x%x File: %ls", pcd->erf.erfOper, pcd->erf.erfType, fileInfo.wzSourcePath);
594 }
595
596 CabcExitOnFailure(hr, "failed to add file to FCI object Oper: 0x%x Type: 0x%x File: %ls", pcd->erf.erfOper, pcd->erf.erfType, fileInfo.wzSourcePath); // TODO: can these be converted to HRESULTS?
597 }
598
599 // For Cabinet Splitting case, check for pcd->hrLastError that may be set as result of Error in CabCGetNextCabinet
600 // This is required as returning False in CabCGetNextCabinet is not aborting cabinet creation and is reporting success instead
601 if (pcd->fCabinetSplittingEnabled && FAILED(pcd->hrLastError))
602 {
603 hr = pcd->hrLastError;
604 CabcExitOnFailure(hr, "Failed to create next cabinet name while splitting cabinet.");
605 }
606
607 if (fFlushAfter && pcd->llBytesSinceLastFlush > pcd->llFlushThreshhold)
608 {
609 if (!::FCIFlushFolder(pcd->hfci, CabCGetNextCabinet, CabCStatus))
610 {
611 CabcExitWithLastError(hr, "failed to flush FCI folder after adding file, Oper: 0x%x Type: 0x%x", pcd->erf.erfOper, pcd->erf.erfType);
612 }
613 pcd->llBytesSinceLastFlush = 0;
614 }
615
616 fFlushAfter = FALSE;
617 fFlushBefore = FALSE;
618 }
619
620 if (!pcd->fGoodCab)
621 {
622 // Prefer our recorded last error, then ::GetLastError(), finally fallback to the useless "E_FAIL" error
623 if (FAILED(pcd->hrLastError))
624 {
625 hr = pcd->hrLastError;
626 }
627 else
628 {
629 CabcExitWithLastError(hr, "failed while creating CAB FCI object Oper: 0x%x Type: 0x%x File: %ls", pcd->erf.erfOper, pcd->erf.erfType, fileInfo.wzSourcePath);
630 }
631
632 CabcExitOnFailure(hr, "failed while creating CAB FCI object Oper: 0x%x Type: 0x%x File: %ls", pcd->erf.erfOper, pcd->erf.erfType, fileInfo.wzSourcePath); // TODO: can these be converted to HRESULTS?
633 }
634
635 // Only flush the cabinet if we actually succeeded in previous calls - otherwise we just waste time (a lot on big cabs)
636 if (!::FCIFlushCabinet(pcd->hfci, FALSE, CabCGetNextCabinet, CabCStatus))
637 {
638 // If we have a last error, use that, otherwise return the useless error
639 hr = FAILED(pcd->hrLastError) ? pcd->hrLastError : E_FAIL;
640 CabcExitOnFailure(hr, "failed to flush FCI object Oper: 0x%x Type: 0x%x", pcd->erf.erfOper, pcd->erf.erfType); // TODO: can these be converted to HRESULTS?
641 }
642
643 if (pcd->fGoodCab && pcd->cDuplicates)
644 {
645 hr = UpdateDuplicateFiles(pcd);
646 CabcExitOnFailure(hr, "Failed to update duplicates in cabinet: %ls", pcd->wzCabinetPath);
647 }
648
649LExit:
650 ::FCIDestroy(pcd->hfci);
651 FreeCabCData(pcd);
652 ReleaseNullStr(pszFileToken);
653
654 return hr;
655}
656
657
658/********************************************************************
659CabCCancel - cancels making a cabinet
660
661NOTE: hContext must be the same used in Begin and AddFile
662*********************************************************************/
663extern "C" void DAPI CabCCancel(
664 __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext
665 )
666{
667 Assert(hContext);
668
669 CABC_DATA* pcd = reinterpret_cast<CABC_DATA*>(hContext);
670 ::FCIDestroy(pcd->hfci);
671 FreeCabCData(pcd);
672}
673
674
675//
676// private
677//
678
679static void FreeCabCData(
680 __in CABC_DATA* pcd
681 )
682{
683 if (pcd)
684 {
685 ReleaseFileHandle(pcd->hEmptyFile);
686
687 for (DWORD i = 0; i < pcd->cFilePaths; ++i)
688 {
689 ReleaseStr(pcd->prgFiles[i].pwzSourcePath);
690 ReleaseMem(pcd->prgFiles[i].pmfHash);
691 }
692 ReleaseMem(pcd->prgFiles);
693 ReleaseMem(pcd->prgDuplicates);
694
695 ReleaseMem(pcd);
696 }
697}
698
699/********************************************************************
700 SmartCab functions
701
702********************************************************************/
703
704static HRESULT CheckForDuplicateFile(
705 __in CABC_DATA *pcd,
706 __out CABC_FILE **ppcf,
707 __in LPCWSTR wzFileName,
708 __in PMSIFILEHASHINFO *ppmfHash,
709 __in LONGLONG llFileSize
710 )
711{
712 DWORD i;
713 HRESULT hr = S_OK;
714 UINT er = ERROR_SUCCESS;
715
716 CabcExitOnNull(ppcf, hr, E_INVALIDARG, "No file structure sent while checking for duplicate file");
717 CabcExitOnNull(ppmfHash, hr, E_INVALIDARG, "No file hash structure pointer sent while checking for duplicate file");
718
719 *ppcf = NULL; // By default, we'll set our output to NULL
720
721 hr = DictGetValue(pcd->shDictHandle, wzFileName, reinterpret_cast<void **>(ppcf));
722 // If we found it in the hash of previously added source paths, return our match immediately
723 if (SUCCEEDED(hr))
724 {
725 ExitFunction1(hr = S_OK);
726 }
727 else if (E_NOTFOUND == hr)
728 {
729 hr = S_OK;
730 }
731 CabcExitOnFailure(hr, "Failed while searching for file in dictionary of previously added files");
732
733 for (i = 0; i < pcd->cFilePaths; ++i)
734 {
735 // If two files have the same size, use hashing to check if they're a match
736 if (llFileSize == pcd->prgFiles[i].llFileSize)
737 {
738 // If pcd->prgFiles[i], our potential match, hasn't been hashed yet, hash it
739 if (pcd->prgFiles[i].pmfHash == NULL)
740 {
741 pcd->prgFiles[i].pmfHash = (PMSIFILEHASHINFO)MemAlloc(sizeof(MSIFILEHASHINFO), FALSE);
742 CabcExitOnNull(pcd->prgFiles[i].pmfHash, hr, E_OUTOFMEMORY, "Failed to allocate memory for candidate duplicate file's MSI file hash");
743
744 pcd->prgFiles[i].pmfHash->dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
745 er = ::MsiGetFileHashW(pcd->prgFiles[i].pwzSourcePath, 0, pcd->prgFiles[i].pmfHash);
746 CabcExitOnWin32Error(er, hr, "Failed while getting MSI file hash of candidate duplicate file: %ls", pcd->prgFiles[i].pwzSourcePath);
747 }
748
749 // If our own file hasn't yet been hashed, hash it
750 if (NULL == *ppmfHash)
751 {
752 *ppmfHash = (PMSIFILEHASHINFO)MemAlloc(sizeof(MSIFILEHASHINFO), FALSE);
753 CabcExitOnNull(*ppmfHash, hr, E_OUTOFMEMORY, "Failed to allocate memory for file's MSI file hash");
754
755 (*ppmfHash)->dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
756 er = ::MsiGetFileHashW(wzFileName, 0, *ppmfHash);
757 CabcExitOnWin32Error(er, hr, "Failed while getting MSI file hash of file: %ls", pcd->prgFiles[i].pwzSourcePath);
758 }
759
760 // If the two file hashes are both of the expected size, and they match, we've got a match, so return it!
761 if (pcd->prgFiles[i].pmfHash->dwFileHashInfoSize == (*ppmfHash)->dwFileHashInfoSize &&
762 sizeof(MSIFILEHASHINFO) == (*ppmfHash)->dwFileHashInfoSize &&
763 pcd->prgFiles[i].pmfHash->dwData[0] == (*ppmfHash)->dwData[0] &&
764 pcd->prgFiles[i].pmfHash->dwData[1] == (*ppmfHash)->dwData[1] &&
765 pcd->prgFiles[i].pmfHash->dwData[2] == (*ppmfHash)->dwData[2] &&
766 pcd->prgFiles[i].pmfHash->dwData[3] == (*ppmfHash)->dwData[3])
767 {
768 *ppcf = pcd->prgFiles + i;
769 ExitFunction1(hr = S_OK);
770 }
771 }
772 }
773
774LExit:
775
776 return hr;
777}
778
779
780static HRESULT AddDuplicateFile(
781 __in CABC_DATA *pcd,
782 __in DWORD dwFileArrayIndex,
783 __in_z LPCWSTR wzSourcePath,
784 __in_opt LPCWSTR wzToken,
785 __in DWORD dwDuplicateCabFileIndex
786 )
787{
788 HRESULT hr = S_OK;
789 LPVOID pv = NULL;
790
791 // Ensure there is enough memory to store this duplicate file index.
792 if (pcd->cDuplicates == pcd->cMaxDuplicates)
793 {
794 pcd->cMaxDuplicates += 20; // grow by a reasonable number (20 is reasonable, right?)
795 size_t cbDuplicates = 0;
796
797 hr = ::SizeTMult(pcd->cMaxDuplicates, sizeof(CABC_DUPLICATEFILE), &cbDuplicates);
798 CabcExitOnFailure(hr, "Maximum allocation exceeded.");
799
800 if (pcd->cDuplicates)
801 {
802 pv = MemReAlloc(pcd->prgDuplicates, cbDuplicates, FALSE);
803 CabcExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to reallocate memory for duplicate file.");
804 }
805 else
806 {
807 pv = MemAlloc(cbDuplicates, FALSE);
808 CabcExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for duplicate file.");
809 }
810
811 ZeroMemory(reinterpret_cast<BYTE*>(pv) + (pcd->cDuplicates * sizeof(CABC_DUPLICATEFILE)), (pcd->cMaxDuplicates - pcd->cDuplicates) * sizeof(CABC_DUPLICATEFILE));
812
813 pcd->prgDuplicates = static_cast<CABC_DUPLICATEFILE*>(pv);
814 pv = NULL;
815 }
816
817 // Store the duplicate file index.
818 pcd->prgDuplicates[pcd->cDuplicates].dwFileArrayIndex = dwFileArrayIndex;
819 pcd->prgDuplicates[pcd->cDuplicates].dwDuplicateCabFileIndex = dwDuplicateCabFileIndex;
820 pcd->prgFiles[dwFileArrayIndex].fHasDuplicates = TRUE; // Mark original file as having duplicates
821
822 hr = StrAllocString(&pcd->prgDuplicates[pcd->cDuplicates].pwzSourcePath, wzSourcePath, 0);
823 CabcExitOnFailure(hr, "Failed to copy duplicate file path: %ls", wzSourcePath);
824
825 if (wzToken && *wzToken)
826 {
827 hr = StrAllocString(&pcd->prgDuplicates[pcd->cDuplicates].pwzToken, wzToken, 0);
828 CabcExitOnFailure(hr, "Failed to copy duplicate file token: %ls", wzToken);
829 }
830
831 ++pcd->cDuplicates;
832
833LExit:
834 ReleaseMem(pv);
835 return hr;
836}
837
838
839static HRESULT AddNonDuplicateFile(
840 __in CABC_DATA *pcd,
841 __in LPCWSTR wzFile,
842 __in_opt LPCWSTR wzToken,
843 __in_opt const MSIFILEHASHINFO* pmfHash,
844 __in LONGLONG llFileSize,
845 __in DWORD dwCabFileIndex
846 )
847{
848 HRESULT hr = S_OK;
849 LPVOID pv = NULL;
850
851 // Ensure there is enough memory to store this file index.
852 if (pcd->cFilePaths == pcd->cMaxFilePaths)
853 {
854 pcd->cMaxFilePaths += 100; // grow by a reasonable number (100 is reasonable, right?)
855 size_t cbFilePaths = 0;
856
857 hr = ::SizeTMult(pcd->cMaxFilePaths, sizeof(CABC_FILE), &cbFilePaths);
858 CabcExitOnFailure(hr, "Maximum allocation exceeded.");
859
860 pv = MemReAlloc(pcd->prgFiles, cbFilePaths, FALSE);
861 CabcExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to reallocate memory for file.");
862
863 ZeroMemory(reinterpret_cast<BYTE*>(pv) + (pcd->cFilePaths * sizeof(CABC_FILE)), (pcd->cMaxFilePaths - pcd->cFilePaths) * sizeof(CABC_FILE));
864
865 pcd->prgFiles = static_cast<CABC_FILE*>(pv);
866 pv = NULL;
867 }
868
869 // Store the file index information.
870 // TODO: add this to a sorted list so we can do a binary search later.
871 CABC_FILE *pcf = pcd->prgFiles + pcd->cFilePaths;
872 pcf->dwCabFileIndex = dwCabFileIndex;
873 pcf->llFileSize = llFileSize;
874
875 if (pmfHash && sizeof(MSIFILEHASHINFO) == pmfHash->dwFileHashInfoSize)
876 {
877 pcf->pmfHash = (PMSIFILEHASHINFO)MemAlloc(sizeof(MSIFILEHASHINFO), FALSE);
878 CabcExitOnNull(pcf->pmfHash, hr, E_OUTOFMEMORY, "Failed to allocate memory for individual file's MSI file hash");
879
880 pcf->pmfHash->dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
881 pcf->pmfHash->dwData[0] = pmfHash->dwData[0];
882 pcf->pmfHash->dwData[1] = pmfHash->dwData[1];
883 pcf->pmfHash->dwData[2] = pmfHash->dwData[2];
884 pcf->pmfHash->dwData[3] = pmfHash->dwData[3];
885 }
886
887 hr = StrAllocString(&pcf->pwzSourcePath, wzFile, 0);
888 CabcExitOnFailure(hr, "Failed to copy file path: %ls", wzFile);
889
890 if (wzToken && *wzToken)
891 {
892 hr = StrAllocString(&pcf->pwzToken, wzToken, 0);
893 CabcExitOnFailure(hr, "Failed to copy file token: %ls", wzToken);
894 }
895
896 ++pcd->cFilePaths;
897
898 hr = DictAddValue(pcd->shDictHandle, pcf);
899 CabcExitOnFailure(hr, "Failed to add file to dictionary of added files");
900
901LExit:
902 ReleaseMem(pv);
903 return hr;
904}
905
906
907static HRESULT UpdateDuplicateFiles(
908 __in const CABC_DATA *pcd
909 )
910{
911 HRESULT hr = S_OK;
912 DWORD cbCabinet = 0;
913 LARGE_INTEGER liCabinetSize = { };
914 HANDLE hCabinet = INVALID_HANDLE_VALUE;
915 HANDLE hCabinetMapping = NULL;
916 LPVOID pv = NULL;
917 MS_CABINET_HEADER *pCabinetHeader = NULL;
918
919 hCabinet = ::CreateFileW(pcd->wzCabinetPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
920 if (INVALID_HANDLE_VALUE == hCabinet)
921 {
922 CabcExitWithLastError(hr, "Failed to open cabinet: %ls", pcd->wzCabinetPath);
923 }
924
925 // Shouldn't need more than 16 MB to get the whole cabinet header into memory so use that as
926 // the upper bound for the memory map.
927 if (!::GetFileSizeEx(hCabinet, &liCabinetSize))
928 {
929 CabcExitWithLastError(hr, "Failed to get size of cabinet: %ls", pcd->wzCabinetPath);
930 }
931
932 if (0 == liCabinetSize.HighPart && liCabinetSize.LowPart < MAX_CABINET_HEADER_SIZE)
933 {
934 cbCabinet = liCabinetSize.LowPart;
935 }
936 else
937 {
938 cbCabinet = MAX_CABINET_HEADER_SIZE;
939 }
940
941 // CreateFileMapping() returns NULL on failure, not INVALID_HANDLE_VALUE
942 hCabinetMapping = ::CreateFileMappingW(hCabinet, NULL, PAGE_READWRITE | SEC_COMMIT, 0, cbCabinet, NULL);
943 if (NULL == hCabinetMapping || INVALID_HANDLE_VALUE == hCabinetMapping)
944 {
945 CabcExitWithLastError(hr, "Failed to memory map cabinet file: %ls", pcd->wzCabinetPath);
946 }
947
948 pv = ::MapViewOfFile(hCabinetMapping, FILE_MAP_WRITE, 0, 0, 0);
949 CabcExitOnNullWithLastError(pv, hr, "Failed to map view of cabinet file: %ls", pcd->wzCabinetPath);
950
951 pCabinetHeader = static_cast<MS_CABINET_HEADER*>(pv);
952
953 for (DWORD i = 0; i < pcd->cDuplicates; ++i)
954 {
955 const CABC_DUPLICATEFILE *pDuplicateFile = pcd->prgDuplicates + i;
956
957 hr = DuplicateFile(pCabinetHeader, pcd, pDuplicateFile);
958 CabcExitOnFailure(hr, "Failed to find cabinet file items at index: %d and %d", pDuplicateFile->dwFileArrayIndex, pDuplicateFile->dwDuplicateCabFileIndex);
959 }
960
961LExit:
962 if (pv)
963 {
964 ::UnmapViewOfFile(pv);
965 }
966 if (hCabinetMapping)
967 {
968 ::CloseHandle(hCabinetMapping);
969 }
970 ReleaseFileHandle(hCabinet);
971
972 return hr;
973}
974
975
976static HRESULT DuplicateFile(
977 __in MS_CABINET_HEADER *pHeader,
978 __in const CABC_DATA *pcd,
979 __in const CABC_DUPLICATEFILE *pDuplicate
980 )
981{
982 HRESULT hr = S_OK;
983 BYTE *pbHeader = reinterpret_cast<BYTE*>(pHeader);
984 BYTE* pbItem = pbHeader + pHeader->coffFiles;
985 const MS_CABINET_ITEM *pOriginalItem = NULL;
986 MS_CABINET_ITEM *pDuplicateItem = NULL;
987
988 if (pHeader->cFiles <= pcd->prgFiles[pDuplicate->dwFileArrayIndex].dwCabFileIndex ||
989 pHeader->cFiles <= pDuplicate->dwDuplicateCabFileIndex ||
990 pDuplicate->dwDuplicateCabFileIndex <= pcd->prgFiles[pDuplicate->dwFileArrayIndex].dwCabFileIndex)
991 {
992 hr = E_UNEXPECTED;
993 CabcExitOnFailure(hr, "Unexpected duplicate file indices, header cFiles: %d, file index: %d, duplicate index: %d", pHeader->cFiles, pcd->prgFiles[pDuplicate->dwFileArrayIndex].dwCabFileIndex, pDuplicate->dwDuplicateCabFileIndex);
994 }
995
996 // Step through each cabinet items until we get to the original
997 // file's index. Notice that the name of the cabinet item is
998 // appended to the end of the MS_CABINET_INFO, that's why we can't
999 // index straight to the data we want.
1000 for (DWORD i = 0; i < pcd->prgFiles[pDuplicate->dwFileArrayIndex].dwCabFileIndex; ++i)
1001 {
1002 LPCSTR szItemName = reinterpret_cast<LPCSTR>(pbItem + sizeof(MS_CABINET_ITEM));
1003 pbItem = pbItem + sizeof(MS_CABINET_ITEM) + lstrlenA(szItemName) + 1;
1004 }
1005
1006 pOriginalItem = reinterpret_cast<const MS_CABINET_ITEM*>(pbItem);
1007
1008 // Now pick up where we left off after the original file's index
1009 // was found and loop until we find the duplicate file's index.
1010 for (DWORD i = pcd->prgFiles[pDuplicate->dwFileArrayIndex].dwCabFileIndex; i < pDuplicate->dwDuplicateCabFileIndex; ++i)
1011 {
1012 LPCSTR szItemName = reinterpret_cast<LPCSTR>(pbItem + sizeof(MS_CABINET_ITEM));
1013 pbItem = pbItem + sizeof(MS_CABINET_ITEM) + lstrlenA(szItemName) + 1;
1014 }
1015
1016 pDuplicateItem = reinterpret_cast<MS_CABINET_ITEM*>(pbItem);
1017
1018 if (0 != pDuplicateItem->cbFile)
1019 {
1020 hr = E_UNEXPECTED;
1021 CabcExitOnFailure(hr, "Failed because duplicate file does not have a file size of zero: %d", pDuplicateItem->cbFile);
1022 }
1023
1024 pDuplicateItem->cbFile = pOriginalItem->cbFile;
1025 pDuplicateItem->uoffFolderStart = pOriginalItem->uoffFolderStart;
1026 pDuplicateItem->iFolder = pOriginalItem->iFolder;
1027 // Note: we do *not* duplicate the date/time and attributes metadata from
1028 // the original item to the duplicate. The following lines are commented
1029 // so people are not tempted to put them back.
1030 //pDuplicateItem->date = pOriginalItem->date;
1031 //pDuplicateItem->time = pOriginalItem->time;
1032 //pDuplicateItem->attribs = pOriginalItem->attribs;
1033
1034LExit:
1035 return hr;
1036}
1037
1038
1039static HRESULT UtcFileTimeToLocalDosDateTime(
1040 __in const FILETIME* pFileTime,
1041 __out USHORT* pDate,
1042 __out USHORT* pTime
1043 )
1044{
1045 HRESULT hr = S_OK;
1046 FILETIME ftLocal = { };
1047
1048 if (!::FileTimeToLocalFileTime(pFileTime, &ftLocal))
1049 {
1050 CabcExitWithLastError(hr, "Filed to convert file time to local file time.");
1051 }
1052
1053 if (!::FileTimeToDosDateTime(&ftLocal, pDate, pTime))
1054 {
1055 CabcExitWithLastError(hr, "Filed to convert file time to DOS date time.");
1056 }
1057
1058LExit:
1059 return hr;
1060}
1061
1062
1063/********************************************************************
1064 FCI callback functions
1065
1066*********************************************************************/
1067static __callback int DIAMONDAPI CabCFilePlaced(
1068 __in PCCAB pccab,
1069 __in_z PSTR szFile,
1070 __in long cbFile,
1071 __in BOOL fContinuation,
1072 __inout_bcount(CABC_HANDLE_BYTES) void *pv
1073 )
1074{
1075 UNREFERENCED_PARAMETER(pccab);
1076 UNREFERENCED_PARAMETER(szFile);
1077 UNREFERENCED_PARAMETER(cbFile);
1078 UNREFERENCED_PARAMETER(fContinuation);
1079 UNREFERENCED_PARAMETER(pv);
1080 return 0;
1081}
1082
1083
1084static __callback void * DIAMONDAPI CabCAlloc(
1085 __in ULONG cb
1086 )
1087{
1088 return MemAlloc(cb, FALSE);
1089}
1090
1091
1092static __callback void DIAMONDAPI CabCFree(
1093 __out_bcount(CABC_HANDLE_BYTES) void *pv
1094 )
1095{
1096 MemFree(pv);
1097}
1098
1099static __callback INT_PTR DIAMONDAPI CabCOpen(
1100 __in_z PSTR pszFile,
1101 __in int oflag,
1102 __in int pmode,
1103 __out int *err,
1104 __inout_bcount(CABC_HANDLE_BYTES) void *pv
1105 )
1106{
1107 CABC_DATA *pcd = reinterpret_cast<CABC_DATA*>(pv);
1108 HRESULT hr = S_OK;
1109 INT_PTR pFile = -1;
1110 DWORD dwAccess = 0;
1111 DWORD dwDisposition = 0;
1112 DWORD dwAttributes = 0;
1113
1114 //
1115 // Translate flags for CreateFile
1116 //
1117 if (oflag & _O_CREAT)
1118 {
1119 if (pmode == _S_IREAD)
1120 dwAccess |= GENERIC_READ;
1121 else if (pmode == _S_IWRITE)
1122 dwAccess |= GENERIC_WRITE;
1123 else if (pmode == (_S_IWRITE | _S_IREAD))
1124 dwAccess |= GENERIC_READ | GENERIC_WRITE;
1125
1126 if (oflag & _O_SHORT_LIVED)
1127 dwDisposition = FILE_ATTRIBUTE_TEMPORARY;
1128 else if (oflag & _O_TEMPORARY)
1129 dwAttributes |= FILE_FLAG_DELETE_ON_CLOSE;
1130 else if (oflag & _O_EXCL)
1131 dwDisposition = CREATE_NEW;
1132 }
1133 if (oflag & _O_TRUNC)
1134 dwDisposition = CREATE_ALWAYS;
1135
1136 if (!dwAccess)
1137 dwAccess = GENERIC_READ;
1138 if (!dwDisposition)
1139 dwDisposition = OPEN_EXISTING;
1140 if (!dwAttributes)
1141 dwAttributes = FILE_ATTRIBUTE_NORMAL;
1142
1143 // Check to see if we were passed the magic character that says 'Unicode string follows'.
1144 if (pszFile && CABC_MAGIC_UNICODE_STRING_MARKER == *pszFile)
1145 {
1146 pFile = reinterpret_cast<INT_PTR>(::CreateFileW(reinterpret_cast<LPCWSTR>(pszFile + 1), dwAccess, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, dwDisposition, dwAttributes, NULL));
1147 }
1148 else
1149 {
1150#pragma prefast(push)
1151#pragma prefast(disable:25068) // We intentionally don't use the unicode API here
1152 pFile = reinterpret_cast<INT_PTR>(::CreateFileA(pszFile, dwAccess, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, dwDisposition, dwAttributes, NULL));
1153#pragma prefast(pop)
1154 }
1155
1156 if (INVALID_HANDLE_VALUE == reinterpret_cast<HANDLE>(pFile))
1157 {
1158 CabcExitOnLastError(hr, "failed to open file: %s", pszFile);
1159 }
1160
1161LExit:
1162 if (FAILED(hr))
1163 pcd->hrLastError = *err = hr;
1164
1165 return FAILED(hr) ? -1 : pFile;
1166}
1167
1168
1169static __callback UINT FAR DIAMONDAPI CabCRead(
1170 __in INT_PTR hf,
1171 __out_bcount(cb) void FAR *memory,
1172 __in UINT cb,
1173 __out int *err,
1174 __inout_bcount(CABC_HANDLE_BYTES) void *pv
1175 )
1176{
1177 CABC_DATA *pcd = reinterpret_cast<CABC_DATA*>(pv);
1178 HRESULT hr = S_OK;
1179 DWORD cbRead = 0;
1180
1181 CabcExitOnNull(hf, *err, E_INVALIDARG, "Failed to read during cabinet extraction because no file handle was provided");
1182 if (!::ReadFile(reinterpret_cast<HANDLE>(hf), memory, cb, &cbRead, NULL))
1183 {
1184 *err = ::GetLastError();
1185 CabcExitOnLastError(hr, "failed to read during cabinet extraction");
1186 }
1187
1188LExit:
1189 if (FAILED(hr))
1190 {
1191 pcd->hrLastError = *err = hr;
1192 }
1193
1194 return FAILED(hr) ? -1 : cbRead;
1195}
1196
1197
1198static __callback UINT FAR DIAMONDAPI CabCWrite(
1199 __in INT_PTR hf,
1200 __in_bcount(cb) void FAR *memory,
1201 __in UINT cb,
1202 __out int *err,
1203 __inout_bcount(CABC_HANDLE_BYTES) void *pv
1204 )
1205{
1206 CABC_DATA *pcd = reinterpret_cast<CABC_DATA*>(pv);
1207 HRESULT hr = S_OK;
1208 DWORD cbWrite = 0;
1209
1210 CabcExitOnNull(hf, *err, E_INVALIDARG, "Failed to write during cabinet extraction because no file handle was provided");
1211 if (!::WriteFile(reinterpret_cast<HANDLE>(hf), memory, cb, &cbWrite, NULL))
1212 {
1213 *err = ::GetLastError();
1214 CabcExitOnLastError(hr, "failed to write during cabinet extraction");
1215 }
1216
1217LExit:
1218 if (FAILED(hr))
1219 pcd->hrLastError = *err = hr;
1220
1221 return FAILED(hr) ? -1 : cbWrite;
1222}
1223
1224
1225static __callback long FAR DIAMONDAPI CabCSeek(
1226 __in INT_PTR hf,
1227 __in long dist,
1228 __in int seektype,
1229 __out int *err,
1230 __inout_bcount(CABC_HANDLE_BYTES) void *pv
1231 )
1232{
1233 CABC_DATA *pcd = reinterpret_cast<CABC_DATA*>(pv);
1234 HRESULT hr = S_OK;
1235 DWORD dwMoveMethod;
1236 LONG lMove = 0;
1237
1238 switch (seektype)
1239 {
1240 case 0: // SEEK_SET
1241 dwMoveMethod = FILE_BEGIN;
1242 break;
1243 case 1: /// SEEK_CUR
1244 dwMoveMethod = FILE_CURRENT;
1245 break;
1246 case 2: // SEEK_END
1247 dwMoveMethod = FILE_END;
1248 break;
1249 default :
1250 dwMoveMethod = 0;
1251 hr = E_UNEXPECTED;
1252 CabcExitOnFailure(hr, "unexpected seektype in FCISeek(): %d", seektype);
1253 }
1254
1255 // SetFilePointer returns -1 if it fails (this will cause FDI to quit with an FDIERROR_USER_ABORT error.
1256 // (Unless this happens while working on a cabinet, in which case FDI returns FDIERROR_CORRUPT_CABINET)
1257 // Todo: update these comments for FCI (are they accurate for FCI as well?)
1258 lMove = ::SetFilePointer(reinterpret_cast<HANDLE>(hf), dist, NULL, dwMoveMethod);
1259 if (DWORD_MAX == lMove)
1260 {
1261 *err = ::GetLastError();
1262 CabcExitOnLastError(hr, "failed to move file pointer %d bytes", dist);
1263 }
1264
1265LExit:
1266 if (FAILED(hr))
1267 {
1268 pcd->hrLastError = *err = hr;
1269 }
1270
1271 return FAILED(hr) ? -1 : lMove;
1272}
1273
1274
1275static __callback int FAR DIAMONDAPI CabCClose(
1276 __in INT_PTR hf,
1277 __out int *err,
1278 __inout_bcount(CABC_HANDLE_BYTES) void *pv
1279 )
1280{
1281 CABC_DATA *pcd = reinterpret_cast<CABC_DATA*>(pv);
1282 HRESULT hr = S_OK;
1283
1284 if (!::CloseHandle(reinterpret_cast<HANDLE>(hf)))
1285 {
1286 *err = ::GetLastError();
1287 CabcExitOnLastError(hr, "failed to close file during cabinet extraction");
1288 }
1289
1290LExit:
1291 if (FAILED(hr))
1292 {
1293 pcd->hrLastError = *err = hr;
1294 }
1295
1296 return FAILED(hr) ? -1 : 0;
1297}
1298
1299static __callback int DIAMONDAPI CabCDelete(
1300 __in_z PSTR szFile,
1301 __out int *err,
1302 __inout_bcount(CABC_HANDLE_BYTES) void *pv
1303 )
1304{
1305 UNREFERENCED_PARAMETER(err);
1306 UNREFERENCED_PARAMETER(pv);
1307
1308#pragma prefast(push)
1309#pragma prefast(disable:25068) // We intentionally don't use the unicode API here
1310 ::DeleteFileA(szFile);
1311#pragma prefast(pop)
1312
1313 return 0;
1314}
1315
1316
1317__success(return != FALSE)
1318static __callback BOOL DIAMONDAPI CabCGetTempFile(
1319 __out_bcount_z(cbFile) char *szFile,
1320 __in int cbFile,
1321 __inout_bcount(CABC_HANDLE_BYTES) void *pv
1322 )
1323{
1324 CABC_DATA *pcd = reinterpret_cast<CABC_DATA*>(pv);
1325 static volatile DWORD dwIndex = 0;
1326
1327 HRESULT hr = S_OK;
1328 char szTempPath[MAX_PATH] = { };
1329 DWORD cchTempPath = MAX_PATH;
1330 DWORD dwProcessId = ::GetCurrentProcessId();
1331 HANDLE hTempFile = INVALID_HANDLE_VALUE;
1332
1333 if (MAX_PATH < ::GetTempPathA(cchTempPath, szTempPath))
1334 {
1335 CabcExitWithLastError(hr, "Failed to get temp path during cabinet creation.");
1336 }
1337
1338 for (DWORD i = 0; i < DWORD_MAX; ++i)
1339 {
1340 LONG dwTempIndex = ::InterlockedIncrement(reinterpret_cast<volatile LONG*>(&dwIndex));
1341
1342 hr = ::StringCbPrintfA(szFile, cbFile, "%s\\%08x.%03x", szTempPath, dwTempIndex, dwProcessId);
1343 CabcExitOnFailure(hr, "failed to format log file path.");
1344
1345 hTempFile = ::CreateFileA(szFile, 0, FILE_SHARE_DELETE, NULL, CREATE_NEW, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL);
1346 if (INVALID_HANDLE_VALUE != hTempFile)
1347 {
1348 // we found one that doesn't exist
1349 hr = S_OK;
1350 break;
1351 }
1352 else
1353 {
1354 hr = E_FAIL; // this file was taken so be pessimistic and assume we're not going to find one.
1355 }
1356 }
1357 CabcExitOnFailure(hr, "failed to find temporary file.");
1358
1359LExit:
1360 ReleaseFileHandle(hTempFile);
1361
1362 if (FAILED(hr))
1363 {
1364 pcd->hrLastError = hr;
1365 }
1366
1367 return FAILED(hr)? FALSE : TRUE;
1368}
1369
1370
1371__success(return != FALSE)
1372static __callback BOOL DIAMONDAPI CabCGetNextCabinet(
1373 __in PCCAB pccab,
1374 __in ULONG ul,
1375 __out_bcount(CABC_HANDLE_BYTES) void *pv
1376 )
1377{
1378 UNREFERENCED_PARAMETER(ul);
1379
1380 // Construct next cab names like cab1a.cab, cab1b.cab, cab1c.cab, ........
1381 CABC_DATA *pcd = reinterpret_cast<CABC_DATA*>(pv);
1382 HRESULT hr = S_OK;
1383 LPWSTR pwzFileToken = NULL;
1384 WCHAR wzNewCabName[MAX_PATH] = L"";
1385
1386 if (pccab->iCab == 1)
1387 {
1388 pcd->wzFirstCabinetName[0] = '\0';
1389 LPCWSTR pwzCabinetName = FileFromPath(pcd->wzCabinetPath);
1390 size_t len = wcsnlen(pwzCabinetName, sizeof(pwzCabinetName));
1391 if (len > 4)
1392 {
1393 len -= 4; // remove Extention ".cab" of 8.3 Format
1394 }
1395 hr = ::StringCchCatNW(pcd->wzFirstCabinetName, countof(pcd->wzFirstCabinetName), pwzCabinetName, len);
1396 CabcExitOnFailure(hr, "Failed to remove extension to create next Cabinet File Name");
1397 }
1398
1399 const int nAlphabets = 26; // Number of Alphabets from a to z
1400 if (pccab->iCab <= nAlphabets)
1401 {
1402 // Construct next cab names like cab1a.cab, cab1b.cab, cab1c.cab, ........
1403 hr = ::StringCchPrintfA(pccab->szCab, sizeof(pccab->szCab), "%ls%c.cab", pcd->wzFirstCabinetName, char(((int)('a') - 1) + pccab->iCab));
1404 CabcExitOnFailure(hr, "Failed to create next Cabinet File Name");
1405 hr = ::StringCchPrintfW(wzNewCabName, countof(wzNewCabName), L"%ls%c.cab", pcd->wzFirstCabinetName, WCHAR(((int)('a') - 1) + pccab->iCab));
1406 CabcExitOnFailure(hr, "Failed to create next Cabinet File Name");
1407 }
1408 else if (pccab->iCab <= nAlphabets*nAlphabets)
1409 {
1410 // Construct next cab names like cab1aa.cab, cab1ab.cab, cab1ac.cab, ......, cabaz.cab, cabaa.cab, cabab.cab, cabac.cab, ......
1411 int char2 = (pccab->iCab) % nAlphabets;
1412 int char1 = (pccab->iCab - char2)/nAlphabets;
1413 if (char2 == 0)
1414 {
1415 // e.g. when iCab = 52, we want az
1416 char2 = nAlphabets; // Second char must be 'z' in this case
1417 char1--; // First Char must be decremented by 1
1418 }
1419 hr = ::StringCchPrintfA(pccab->szCab, sizeof(pccab->szCab), "%ls%c%c.cab", pcd->wzFirstCabinetName, char(((int)('a') - 1) + char1), char(((int)('a') - 1) + char2));
1420 CabcExitOnFailure(hr, "Failed to create next Cabinet File Name");
1421 hr = ::StringCchPrintfW(wzNewCabName, countof(wzNewCabName), L"%ls%c%c.cab", pcd->wzFirstCabinetName, WCHAR(((int)('a') - 1) + char1), WCHAR(((int)('a') - 1) + char2));
1422 CabcExitOnFailure(hr, "Failed to create next Cabinet File Name");
1423 }
1424 else
1425 {
1426 hr = DISP_E_BADINDEX; // Value 0x8002000B stands for Invalid index.
1427 CabcExitOnFailure(hr, "Cannot Split Cabinet more than 26*26 = 676 times. Failed to create next Cabinet File Name");
1428 }
1429
1430 // Callback from PFNFCIGETNEXTCABINET CabCGetNextCabinet method
1431 if(pcd->fileSplitCabNamesCallback != 0)
1432 {
1433 // In following if/else block, getting the Token for the First File in the Cabinets that are getting Split
1434 // This code will need updation if we need to send all file tokens for the splitting Cabinets
1435 if (pcd->prgFiles[0].pwzToken)
1436 {
1437 pwzFileToken = pcd->prgFiles[0].pwzToken;
1438 }
1439 else
1440 {
1441 LPCWSTR wzSourcePath = pcd->prgFiles[0].pwzSourcePath;
1442 pwzFileToken = FileFromPath(wzSourcePath);
1443 }
1444
1445 // The call back to Binder to Add File Transfer for new Cab and add new Cab to Media table
1446 pcd->fileSplitCabNamesCallback(pcd->wzFirstCabinetName, wzNewCabName, pwzFileToken);
1447 }
1448
1449LExit:
1450 if (FAILED(hr))
1451 {
1452 // Returning False in case of error here as stated by Documentation, However It fails to Abort Cab Creation!!!
1453 // So Using separate check for pcd->hrLastError after ::FCIAddFile for Cabinet Splitting
1454 pcd->hrLastError = hr;
1455 return FALSE;
1456 }
1457 else
1458 {
1459 return TRUE;
1460 }
1461}
1462
1463
1464static __callback INT_PTR DIAMONDAPI CabCGetOpenInfo(
1465 __in_z PSTR pszName,
1466 __out USHORT *pdate,
1467 __out USHORT *ptime,
1468 __out USHORT *pattribs,
1469 __out int *err,
1470 __out_bcount(CABC_HANDLE_BYTES) void *pv
1471 )
1472{
1473 HRESULT hr = S_OK;
1474 CABC_INTERNAL_ADDFILEINFO* pFileInfo = reinterpret_cast<CABC_INTERNAL_ADDFILEINFO*>(pszName);
1475 LPCWSTR wzFile = NULL;
1476 DWORD cbFile = 0;
1477 LPSTR pszFilePlusMagic = NULL;
1478 DWORD cbFilePlusMagic = 0;
1479 WIN32_FILE_ATTRIBUTE_DATA fad = { };
1480 INT_PTR iResult = -1;
1481
1482 // If there is an empty file provided, use that as the source path to cab (since we
1483 // must be dealing with a duplicate file). Otherwise, use the source path you'd expect.
1484 wzFile = pFileInfo->wzEmptyPath ? pFileInfo->wzEmptyPath : pFileInfo->wzSourcePath;
1485 cbFile = (lstrlenW(wzFile) + 1) * sizeof(WCHAR);
1486
1487 // Convert the source file path into an Ansi string that our APIs will recognize as
1488 // a Unicode string (due to the magic character).
1489 cbFilePlusMagic = cbFile + 1; // add one for the magic.
1490 pszFilePlusMagic = reinterpret_cast<LPSTR>(MemAlloc(cbFilePlusMagic, TRUE));
1491
1492 *pszFilePlusMagic = CABC_MAGIC_UNICODE_STRING_MARKER;
1493 memcpy_s(pszFilePlusMagic + 1, cbFilePlusMagic - 1, wzFile, cbFile);
1494
1495 if (!::GetFileAttributesExW(pFileInfo->wzSourcePath, GetFileExInfoStandard, &fad))
1496 {
1497 CabcExitWithLastError(hr, "Failed to get file attributes on '%ls'.", pFileInfo->wzSourcePath);
1498 }
1499
1500 // Set the attributes but only allow the few attributes that CAB supports.
1501 *pattribs = static_cast<USHORT>(fad.dwFileAttributes) & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE);
1502
1503 hr = UtcFileTimeToLocalDosDateTime(&fad.ftLastWriteTime, pdate, ptime);
1504 if (FAILED(hr))
1505 {
1506 // NOTE: Changed this from ftLastWriteTime to ftCreationTime because of issues around how different OSs were
1507 // handling the access of the FILETIME structure and how it would fail conversion to DOS time if it wasn't
1508 // found. This would create further problems if the file was written to the CAB without this value. Windows
1509 // Installer would then fail to extract the file.
1510 hr = UtcFileTimeToLocalDosDateTime(&fad.ftCreationTime, pdate, ptime);
1511 CabcExitOnFailure(hr, "Filed to read a valid file time stucture on file '%s'.", pszName);
1512 }
1513
1514 iResult = CabCOpen(pszFilePlusMagic, _O_BINARY|_O_RDONLY, 0, err, pv);
1515
1516LExit:
1517 ReleaseMem(pszFilePlusMagic);
1518 if (FAILED(hr))
1519 {
1520 *err = (int)hr;
1521 }
1522
1523 return FAILED(hr) ? -1 : iResult;
1524}
1525
1526
1527static __callback long DIAMONDAPI CabCStatus(
1528 __in UINT ui,
1529 __in ULONG cb1,
1530 __in ULONG cb2,
1531 __inout_bcount(CABC_HANDLE_BYTES) void *pv
1532 )
1533{
1534 UNREFERENCED_PARAMETER(ui);
1535 UNREFERENCED_PARAMETER(cb1);
1536 UNREFERENCED_PARAMETER(cb2);
1537 UNREFERENCED_PARAMETER(pv);
1538 return 0;
1539}
diff --git a/src/libs/dutil/WixToolset.DUtil/cabutil.cpp b/src/libs/dutil/WixToolset.DUtil/cabutil.cpp
new file mode 100644
index 00000000..5d77e483
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/cabutil.cpp
@@ -0,0 +1,617 @@
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// Exit macros
7#define CabExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_CABUTIL, x, s, __VA_ARGS__)
8#define CabExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_CABUTIL, x, s, __VA_ARGS__)
9#define CabExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_CABUTIL, x, s, __VA_ARGS__)
10#define CabExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_CABUTIL, x, s, __VA_ARGS__)
11#define CabExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_CABUTIL, x, s, __VA_ARGS__)
12#define CabExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_CABUTIL, x, s, __VA_ARGS__)
13#define CabExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_CABUTIL, p, x, e, s, __VA_ARGS__)
14#define CabExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_CABUTIL, p, x, s, __VA_ARGS__)
15#define CabExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_CABUTIL, p, x, e, s, __VA_ARGS__)
16#define CabExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_CABUTIL, p, x, s, __VA_ARGS__)
17#define CabExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_CABUTIL, e, x, s, __VA_ARGS__)
18#define CabExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_CABUTIL, g, x, s, __VA_ARGS__)
19
20
21// external prototypes
22typedef BOOL (FAR DIAMONDAPI *PFNFDIDESTROY)(VOID*);
23typedef HFDI (FAR DIAMONDAPI *PFNFDICREATE)(PFNALLOC, PFNFREE, PFNOPEN, PFNREAD, PFNWRITE, PFNCLOSE, PFNSEEK, int, PERF);
24typedef BOOL (FAR DIAMONDAPI *PFNFDIISCABINET)(HFDI, INT_PTR, PFDICABINETINFO);
25typedef BOOL (FAR DIAMONDAPI *PFNFDICOPY)(HFDI, char *, char *, int, PFNFDINOTIFY, PFNFDIDECRYPT, void *);
26
27
28//
29// static globals
30//
31static HMODULE vhCabinetDll = NULL;
32
33static HFDI vhfdi = NULL;
34static PFNFDICREATE vpfnFDICreate = NULL;
35static PFNFDICOPY vpfnFDICopy = NULL;
36static PFNFDIISCABINET vpfnFDIIsCabinet = NULL;
37static PFNFDIDESTROY vpfnFDIDestroy = NULL;
38static ERF verf;
39
40static DWORD64 vdw64EmbeddedOffset = 0;
41
42//
43// structs
44//
45struct CAB_CALLBACK_STRUCT
46{
47 BOOL fStopExtracting; // flag set when no more files are needed
48 LPCWSTR pwzExtract; // file to extract ("*" means extract all)
49 LPCWSTR pwzExtractDir; // directory to extract files to
50
51 // possible user data
52 CAB_CALLBACK_PROGRESS pfnProgress;
53 LPVOID pvContext;
54};
55
56//
57// prototypes
58//
59static __callback LPVOID DIAMONDAPI CabExtractAlloc(__in DWORD dwSize);
60static __callback void DIAMONDAPI CabExtractFree(__in LPVOID pvData);
61static __callback INT_PTR FAR DIAMONDAPI CabExtractOpen(__in_z PSTR pszFile, __in int oflag, __in int pmode);
62static __callback UINT FAR DIAMONDAPI CabExtractRead(__in INT_PTR hf, __out void FAR *pv, __in UINT cb);
63static __callback UINT FAR DIAMONDAPI CabExtractWrite(__in INT_PTR hf, __in void FAR *pv, __in UINT cb);
64static __callback int FAR DIAMONDAPI CabExtractClose(__in INT_PTR hf);
65static __callback long FAR DIAMONDAPI CabExtractSeek(__in INT_PTR hf, __in long dist, __in int seektype);
66static __callback INT_PTR DIAMONDAPI CabExtractCallback(__in FDINOTIFICATIONTYPE iNotification, __inout FDINOTIFICATION *pFDINotify);
67static HRESULT DAPI CabOperation(__in LPCWSTR wzCabinet, __in LPCWSTR wzExtractFile, __in_opt LPCWSTR wzExtractDir, __in_opt CAB_CALLBACK_PROGRESS pfnProgress, __in_opt LPVOID pvContext, __in_opt STDCALL_PFNFDINOTIFY pfnNotify, __in DWORD64 dw64EmbeddedOffset);
68
69static STDCALL_PFNFDINOTIFY v_pfnNetFx11Notify = NULL;
70
71
72inline HRESULT LoadCabinetDll()
73{
74 HRESULT hr = S_OK;
75 if (!vhCabinetDll)
76 {
77 hr = LoadSystemLibrary(L"cabinet.dll", &vhCabinetDll);
78 CabExitOnFailure(hr, "failed to load cabinet.dll");
79
80 // retrieve all address functions
81 vpfnFDICreate = reinterpret_cast<PFNFDICREATE>(::GetProcAddress(vhCabinetDll, "FDICreate"));
82 CabExitOnNullWithLastError(vpfnFDICreate, hr, "failed to import FDICreate from CABINET.DLL");
83 vpfnFDICopy = reinterpret_cast<PFNFDICOPY>(::GetProcAddress(vhCabinetDll, "FDICopy"));
84 CabExitOnNullWithLastError(vpfnFDICopy, hr, "failed to import FDICopy from CABINET.DLL");
85 vpfnFDIIsCabinet = reinterpret_cast<PFNFDIISCABINET>(::GetProcAddress(vhCabinetDll, "FDIIsCabinet"));
86 CabExitOnNullWithLastError(vpfnFDIIsCabinet, hr, "failed to import FDIIsCabinetfrom CABINET.DLL");
87 vpfnFDIDestroy = reinterpret_cast<PFNFDIDESTROY>(::GetProcAddress(vhCabinetDll, "FDIDestroy"));
88 CabExitOnNullWithLastError(vpfnFDIDestroy, hr, "failed to import FDIDestroyfrom CABINET.DLL");
89
90 vhfdi = vpfnFDICreate(CabExtractAlloc, CabExtractFree, CabExtractOpen, CabExtractRead, CabExtractWrite, CabExtractClose, CabExtractSeek, cpuUNKNOWN, &verf);
91 CabExitOnNull(vhfdi, hr, E_FAIL, "failed to initialize cabinet.dll");
92 }
93
94LExit:
95 if (FAILED(hr) && vhCabinetDll)
96 {
97 ::FreeLibrary(vhCabinetDll);
98 vhCabinetDll = NULL;
99 }
100
101 return hr;
102}
103
104
105static HANDLE OpenFileWithRetry(
106 __in LPCWSTR wzPath,
107 __in DWORD dwDesiredAccess,
108 __in DWORD dwCreationDisposition
109)
110{
111 HANDLE hFile = INVALID_HANDLE_VALUE;
112
113 for (DWORD i = 0; i < 30; ++i)
114 {
115 hFile = ::CreateFileW(wzPath, dwDesiredAccess, FILE_SHARE_READ, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL);
116 if (INVALID_HANDLE_VALUE != hFile)
117 {
118 break;
119 }
120
121 ::Sleep(100);
122 }
123
124 return hFile;
125}
126
127
128/********************************************************************
129 CabInitialize - initializes internal static variables
130
131********************************************************************/
132extern "C" HRESULT DAPI CabInitialize(
133 __in BOOL fDelayLoad
134 )
135{
136 HRESULT hr = S_OK;
137
138 if (!fDelayLoad)
139 {
140 hr = LoadCabinetDll();
141 CabExitOnFailure(hr, "failed to load CABINET.DLL");
142 }
143
144LExit:
145 return hr;
146}
147
148
149/********************************************************************
150 CabUninitialize - initializes internal static variables
151
152********************************************************************/
153extern "C" void DAPI CabUninitialize(
154 )
155{
156 if (vhfdi)
157 {
158 if (vpfnFDIDestroy)
159 {
160 vpfnFDIDestroy(vhfdi);
161 }
162 vhfdi = NULL;
163 }
164
165 vpfnFDICreate = NULL;
166 vpfnFDICopy =NULL;
167 vpfnFDIIsCabinet = NULL;
168 vpfnFDIDestroy = NULL;
169
170 if (vhCabinetDll)
171 {
172 ::FreeLibrary(vhCabinetDll);
173 vhCabinetDll = NULL;
174 }
175}
176
177/********************************************************************
178 CabEnumerate - list files inside cabinet
179
180 NOTE: wzCabinet must be full path to cabinet file
181 pfnNotify is callback function to get notified for each file
182 in the cabinet
183********************************************************************/
184extern "C" HRESULT DAPI CabEnumerate(
185 __in_z LPCWSTR wzCabinet,
186 __in_z LPCWSTR wzEnumerateFile,
187 __in STDCALL_PFNFDINOTIFY pfnNotify,
188 __in DWORD64 dw64EmbeddedOffset
189 )
190{
191 return CabOperation(wzCabinet, wzEnumerateFile, NULL, NULL, NULL, pfnNotify, dw64EmbeddedOffset);
192}
193
194/********************************************************************
195 CabExtract - extracts one or all files from a cabinet
196
197 NOTE: wzCabinet must be full path to cabinet file
198 wzExtractFile can be a single file id or "*" to extract all files
199 wzExttractDir must be normalized (end in a "\")
200 if pfnBeginFile is NULL pfnEndFile must be NULL and vice versa
201********************************************************************/
202extern "C" HRESULT DAPI CabExtract(
203 __in_z LPCWSTR wzCabinet,
204 __in_z LPCWSTR wzExtractFile,
205 __in_z LPCWSTR wzExtractDir,
206 __in_opt CAB_CALLBACK_PROGRESS pfnProgress,
207 __in_opt LPVOID pvContext,
208 __in DWORD64 dw64EmbeddedOffset
209 )
210{
211 return CabOperation(wzCabinet, wzExtractFile, wzExtractDir, pfnProgress, pvContext, NULL, dw64EmbeddedOffset);
212}
213
214//
215// private
216//
217/********************************************************************
218 FDINotify -- wrapper that converts call convention from __cdecl to __stdcall.
219
220 NOTE: Since netfx 1.1 supports only function pointers (delegates)
221 with __stdcall calling convention and cabinet api uses
222 __cdecl calling convention, we need this wrapper function.
223 netfx 2.0 will work with [UnmanagedFunctionPointer(CallingConvention.Cdecl)] attribute on the delegate.
224 TODO: remove this when upgrading to netfx 2.0.
225********************************************************************/
226static __callback INT_PTR DIAMONDAPI FDINotify(
227 __in FDINOTIFICATIONTYPE iNotification,
228 __inout FDINOTIFICATION *pFDINotify
229 )
230{
231 if (NULL != v_pfnNetFx11Notify)
232 {
233 return v_pfnNetFx11Notify(iNotification, pFDINotify);
234 }
235 else
236 {
237 return (INT_PTR)0;
238 }
239}
240
241
242/********************************************************************
243 CabOperation - helper function that enumerates or extracts files
244 from cabinet
245
246 NOTE: wzCabinet must be full path to cabinet file
247 wzExtractFile can be a single file id or "*" to extract all files
248 wzExttractDir must be normalized (end in a "\")
249 if pfnBeginFile is NULL pfnEndFile must be NULL and vice versa
250 pfnNotify is callback function to get notified for each file
251 in the cabinet. If it's NULL, files will be extracted.
252********************************************************************/
253static HRESULT DAPI CabOperation(
254 __in LPCWSTR wzCabinet,
255 __in LPCWSTR wzExtractFile,
256 __in_opt LPCWSTR wzExtractDir,
257 __in_opt CAB_CALLBACK_PROGRESS pfnProgress,
258 __in_opt LPVOID pvContext,
259 __in_opt STDCALL_PFNFDINOTIFY pfnNotify,
260 __in DWORD64 dw64EmbeddedOffset
261 )
262{
263 HRESULT hr = S_OK;
264 BOOL fResult;
265
266 LPWSTR sczCabinet = NULL;
267 LPWSTR pwz = NULL;
268 CHAR szCabDirectory[MAX_PATH * 4]; // Make sure these are big enough for UTF-8 strings
269 CHAR szCabFile[MAX_PATH * 4];
270
271 CAB_CALLBACK_STRUCT ccs;
272 PFNFDINOTIFY pfnFdiNotify;
273
274 //
275 // ensure the cabinet.dll is loaded
276 //
277 if (!vhfdi)
278 {
279 hr = LoadCabinetDll();
280 CabExitOnFailure(hr, "failed to load CABINET.DLL");
281 }
282
283 hr = StrAllocString(&sczCabinet, wzCabinet, 0);
284 CabExitOnFailure(hr, "Failed to make copy of cabinet name:%ls", wzCabinet);
285
286 //
287 // split the cabinet full path into directory and filename and convert to multi-byte (ick!)
288 //
289 pwz = FileFromPath(sczCabinet);
290 CabExitOnNull(pwz, hr, E_INVALIDARG, "failed to process cabinet path: %ls", wzCabinet);
291
292 if (!::WideCharToMultiByte(CP_UTF8, 0, pwz, -1, szCabFile, countof(szCabFile), NULL, NULL))
293 {
294 CabExitWithLastError(hr, "failed to convert cabinet filename to ASCII: %ls", pwz);
295 }
296
297 *pwz = '\0';
298
299 // If a full path was not provided, use the relative current directory.
300 if (wzCabinet == pwz)
301 {
302 hr = ::StringCchCopyA(szCabDirectory, countof(szCabDirectory), ".\\");
303 CabExitOnFailure(hr, "Failed to copy relative current directory as cabinet directory.");
304 }
305 else
306 {
307 if (!::WideCharToMultiByte(CP_UTF8, 0, sczCabinet, -1, szCabDirectory, countof(szCabDirectory), NULL, NULL))
308 {
309 CabExitWithLastError(hr, "failed to convert cabinet directory to ASCII: %ls", sczCabinet);
310 }
311 }
312
313 //
314 // iterate through files in cabinet extracting them to the callback function
315 //
316 ccs.fStopExtracting = FALSE;
317 ccs.pwzExtract = wzExtractFile;
318 ccs.pwzExtractDir = wzExtractDir;
319 ccs.pfnProgress = pfnProgress;
320 ccs.pvContext = pvContext;
321
322 vdw64EmbeddedOffset = dw64EmbeddedOffset;
323
324 // if pfnNotify is given, use it, otherwise use default callback
325 if (NULL == pfnNotify)
326 {
327 pfnFdiNotify = CabExtractCallback;
328 }
329 else
330 {
331 v_pfnNetFx11Notify = pfnNotify;
332 pfnFdiNotify = FDINotify;
333 }
334 fResult = vpfnFDICopy(vhfdi, szCabFile, szCabDirectory, 0, pfnFdiNotify, NULL, static_cast<void*>(&ccs));
335 if (!fResult && !ccs.fStopExtracting) // if something went wrong and it wasn't us just stopping the extraction, then return a failure
336 {
337 CabExitWithLastError(hr, "failed to extract cabinet file: %ls", sczCabinet);
338 }
339
340LExit:
341 ReleaseStr(sczCabinet);
342 v_pfnNetFx11Notify = NULL;
343
344 return hr;
345}
346
347/****************************************************************************
348 default extract routines
349
350****************************************************************************/
351static __callback LPVOID DIAMONDAPI CabExtractAlloc(__in DWORD dwSize)
352{
353 return MemAlloc(dwSize, FALSE);
354}
355
356
357static __callback void DIAMONDAPI CabExtractFree(__in LPVOID pvData)
358{
359 MemFree(pvData);
360}
361
362
363static __callback INT_PTR FAR DIAMONDAPI CabExtractOpen(__in_z PSTR pszFile, __in int oflag, __in int pmode)
364{
365 HRESULT hr = S_OK;
366 HANDLE hFile = INVALID_HANDLE_VALUE;
367 INT_PTR pFile = -1;
368 LPWSTR sczCabFile = NULL;
369
370 // if FDI asks for some unusual mode (in low memory situation it could ask for a scratch file) fail
371 if ((oflag != (/*_O_BINARY*/ 0x8000 | /*_O_RDONLY*/ 0x0000)) || (pmode != (_S_IREAD | _S_IWRITE)))
372 {
373 hr = E_OUTOFMEMORY;
374 CabExitOnFailure(hr, "FDI asked for a scratch file to be created, which is unsupported");
375 }
376
377 hr = StrAllocStringAnsi(&sczCabFile, pszFile, 0, CP_UTF8);
378 CabExitOnFailure(hr, "Failed to convert UTF8 cab file name to wide character string");
379
380 hFile = OpenFileWithRetry(sczCabFile, GENERIC_READ, OPEN_EXISTING);
381 if (INVALID_HANDLE_VALUE == hFile)
382 {
383 CabExitWithLastError(hr, "failed to open file: %ls", sczCabFile);
384 }
385
386 pFile = reinterpret_cast<INT_PTR>(hFile);
387
388 if (vdw64EmbeddedOffset)
389 {
390 hr = CabExtractSeek(pFile, 0, 0);
391 CabExitOnFailure(hr, "Failed to seek to embedded offset %I64d", vdw64EmbeddedOffset);
392 }
393
394 hFile = INVALID_HANDLE_VALUE;
395
396LExit:
397 ReleaseFileHandle(hFile);
398 ReleaseStr(sczCabFile);
399
400 return FAILED(hr) ? -1 : pFile;
401}
402
403
404static __callback UINT FAR DIAMONDAPI CabExtractRead(__in INT_PTR hf, __out void FAR *pv, __in UINT cb)
405{
406 HRESULT hr = S_OK;
407 DWORD cbRead = 0;
408
409 CabExitOnNull(hf, hr, E_INVALIDARG, "Failed to read file during cabinet extraction - no file given to read");
410 if (!::ReadFile(reinterpret_cast<HANDLE>(hf), pv, cb, &cbRead, NULL))
411 {
412 CabExitWithLastError(hr, "failed to read during cabinet extraction");
413 }
414
415LExit:
416 return FAILED(hr) ? -1 : cbRead;
417}
418
419
420static __callback UINT FAR DIAMONDAPI CabExtractWrite(__in INT_PTR hf, __in void FAR *pv, __in UINT cb)
421{
422 HRESULT hr = S_OK;
423 DWORD cbWrite = 0;
424
425 CabExitOnNull(hf, hr, E_INVALIDARG, "Failed to write file during cabinet extraction - no file given to write");
426 if (!::WriteFile(reinterpret_cast<HANDLE>(hf), pv, cb, &cbWrite, NULL))
427 {
428 CabExitWithLastError(hr, "failed to write during cabinet extraction");
429 }
430
431LExit:
432 return FAILED(hr) ? -1 : cbWrite;
433}
434
435
436static __callback long FAR DIAMONDAPI CabExtractSeek(__in INT_PTR hf, __in long dist, __in int seektype)
437{
438 HRESULT hr = S_OK;
439 DWORD dwMoveMethod;
440 LONG lMove = 0;
441
442 switch (seektype)
443 {
444 case 0: // SEEK_SET
445 dwMoveMethod = FILE_BEGIN;
446 dist += static_cast<long>(vdw64EmbeddedOffset);
447 break;
448 case 1: /// SEEK_CUR
449 dwMoveMethod = FILE_CURRENT;
450 break;
451 case 2: // SEEK_END
452 dwMoveMethod = FILE_END;
453 break;
454 default :
455 dwMoveMethod = 0;
456 hr = E_UNEXPECTED;
457 CabExitOnFailure(hr, "unexpected seektype in FDISeek(): %d", seektype);
458 }
459
460 // SetFilePointer returns -1 if it fails (this will cause FDI to quit with an FDIERROR_USER_ABORT error.
461 // (Unless this happens while working on a cabinet, in which case FDI returns FDIERROR_CORRUPT_CABINET)
462 lMove = ::SetFilePointer(reinterpret_cast<HANDLE>(hf), dist, NULL, dwMoveMethod);
463 if (0xFFFFFFFF == lMove)
464 {
465 CabExitWithLastError(hr, "failed to move file pointer %d bytes", dist);
466 }
467
468LExit:
469 return FAILED(hr) ? -1 : lMove - static_cast<long>(vdw64EmbeddedOffset);
470}
471
472
473static __callback int FAR DIAMONDAPI CabExtractClose(__in INT_PTR hf)
474{
475 HRESULT hr = S_OK;
476
477 if (!::CloseHandle(reinterpret_cast<HANDLE>(hf)))
478 {
479 CabExitWithLastError(hr, "failed to close file during cabinet extraction");
480 }
481
482LExit:
483 return FAILED(hr) ? -1 : 0;
484}
485
486
487static __callback INT_PTR DIAMONDAPI CabExtractCallback(__in FDINOTIFICATIONTYPE iNotification, __inout FDINOTIFICATION *pFDINotify)
488{
489 Assert(pFDINotify->pv);
490
491 HRESULT hr = S_OK;
492 HANDLE hFile = INVALID_HANDLE_VALUE;
493 INT_PTR ipResult = 0; // result to return on success
494
495 CAB_CALLBACK_STRUCT* pccs = static_cast<CAB_CALLBACK_STRUCT*>(pFDINotify->pv);
496 LPCSTR sz;
497 WCHAR wz[MAX_PATH];
498 FILETIME ft;
499
500 switch (iNotification)
501 {
502 case fdintCOPY_FILE: // begin extracting a resource from cabinet
503 CabExitOnNull(pFDINotify->psz1, hr, E_INVALIDARG, "No cabinet file ID given to convert");
504 CabExitOnNull(pccs, hr, E_INVALIDARG, "Failed to call cabextract callback, because no callback struct was provided");
505
506 if (pccs->fStopExtracting)
507 {
508 ExitFunction1(hr = S_FALSE); // no more extracting
509 }
510
511 // convert params to useful variables
512 sz = static_cast<LPCSTR>(pFDINotify->psz1);
513 if (!::MultiByteToWideChar(CP_ACP, 0, sz, -1, wz, countof(wz)))
514 {
515 CabExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz);
516 }
517
518 if (pccs->pfnProgress)
519 {
520 hr = pccs->pfnProgress(TRUE, wz, pccs->pvContext);
521 if (S_OK != hr)
522 {
523 ExitFunction();
524 }
525 }
526
527 if (L'*' == *pccs->pwzExtract || 0 == lstrcmpW(pccs->pwzExtract, wz))
528 {
529 // get the created date for the resource in the cabinet
530 FILETIME ftLocal;
531 if (!::DosDateTimeToFileTime(pFDINotify->date, pFDINotify->time, &ftLocal))
532 {
533 CabExitWithLastError(hr, "failed to get time for resource: %ls", wz);
534 }
535 ::LocalFileTimeToFileTime(&ftLocal, &ft);
536
537 WCHAR wzPath[MAX_PATH];
538 hr = ::StringCchCopyW(wzPath, countof(wzPath), pccs->pwzExtractDir);
539 CabExitOnFailure(hr, "failed to copy in extract directory: %ls for file: %ls", pccs->pwzExtractDir, wz);
540 hr = ::StringCchCatW(wzPath, countof(wzPath), wz);
541 CabExitOnFailure(hr, "failed to concat onto path: %ls file: %ls", wzPath, wz);
542
543 hFile = OpenFileWithRetry(wzPath, GENERIC_WRITE, CREATE_ALWAYS);
544 if (INVALID_HANDLE_VALUE == hFile)
545 {
546 CabExitWithLastError(hr, "failed to create file: %ls", wzPath);
547 }
548
549 ::SetFileTime(hFile, &ft, &ft, &ft); // try to set the file time (who cares if it fails)
550
551 if (::SetFilePointer(hFile, pFDINotify->cb, NULL, FILE_BEGIN)) // try to set the end of the file (don't worry if this fails)
552 {
553 if (::SetEndOfFile(hFile))
554 {
555 ::SetFilePointer(hFile, 0, NULL, FILE_BEGIN); // reset the file pointer
556 }
557 }
558
559 ipResult = reinterpret_cast<INT_PTR>(hFile);
560 hFile = INVALID_HANDLE_VALUE;
561 }
562 else // resource wasn't requested, skip it
563 {
564 hr = S_OK;
565 ipResult = 0;
566 }
567
568 break;
569 case fdintCLOSE_FILE_INFO: // resource extraction complete
570 Assert(pFDINotify->hf && pFDINotify->psz1);
571 CabExitOnNull(pccs, hr, E_INVALIDARG, "Failed to call cabextract callback, because no callback struct was provided");
572
573 // convert params to useful variables
574 sz = static_cast<LPCSTR>(pFDINotify->psz1);
575 CabExitOnNull(sz, hr, E_INVALIDARG, "Failed to convert cabinet file id, because no cabinet file id was provided");
576
577 if (!::MultiByteToWideChar(CP_ACP, 0, sz, -1, wz, countof(wz)))
578 {
579 CabExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz);
580 }
581
582 if (NULL != pFDINotify->hf) // just close the file
583 {
584 ::CloseHandle(reinterpret_cast<HANDLE>(pFDINotify->hf));
585 }
586
587 if (pccs->pfnProgress)
588 {
589 hr = pccs->pfnProgress(FALSE, wz, pccs->pvContext);
590 }
591
592 if (S_OK == hr && L'*' == *pccs->pwzExtract) // if everything is okay and we're extracting all files, keep going
593 {
594 ipResult = TRUE;
595 }
596 else // something went wrong or we only needed to extract one file
597 {
598 hr = S_OK;
599 ipResult = FALSE;
600 pccs->fStopExtracting = TRUE;
601 }
602
603 break;
604 case fdintPARTIAL_FILE: __fallthrough; // no action needed for these messages, fall through
605 case fdintNEXT_CABINET: __fallthrough;
606 case fdintENUMERATE: __fallthrough;
607 case fdintCABINET_INFO:
608 break;
609 default:
610 AssertSz(FALSE, "CabExtractCallback() - unknown FDI notification command");
611 };
612
613LExit:
614 ReleaseFileHandle(hFile);
615
616 return (S_OK == hr) ? ipResult : -1;
617}
diff --git a/src/libs/dutil/WixToolset.DUtil/certutil.cpp b/src/libs/dutil/WixToolset.DUtil/certutil.cpp
new file mode 100644
index 00000000..69897b9e
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/certutil.cpp
@@ -0,0 +1,342 @@
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// Exit macros
7#define CertExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_CERTUTIL, x, s, __VA_ARGS__)
8#define CertExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_CERTUTIL, x, s, __VA_ARGS__)
9#define CertExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_CERTUTIL, x, s, __VA_ARGS__)
10#define CertExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_CERTUTIL, x, s, __VA_ARGS__)
11#define CertExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_CERTUTIL, x, s, __VA_ARGS__)
12#define CertExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_CERTUTIL, x, s, __VA_ARGS__)
13#define CertExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_CERTUTIL, p, x, e, s, __VA_ARGS__)
14#define CertExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_CERTUTIL, p, x, s, __VA_ARGS__)
15#define CertExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_CERTUTIL, p, x, e, s, __VA_ARGS__)
16#define CertExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_CERTUTIL, p, x, s, __VA_ARGS__)
17#define CertExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_CERTUTIL, e, x, s, __VA_ARGS__)
18#define CertExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_CERTUTIL, g, x, s, __VA_ARGS__)
19
20/********************************************************************
21CertReadProperty - reads a property from the certificate.
22
23NOTE: call MemFree() on the returned pvValue.
24********************************************************************/
25extern "C" HRESULT DAPI CertReadProperty(
26 __in PCCERT_CONTEXT pCertContext,
27 __in DWORD dwProperty,
28 __deref_out_bound LPVOID* ppvValue,
29 __out_opt DWORD* pcbValue
30 )
31{
32 HRESULT hr = S_OK;
33 LPVOID pv = NULL;
34 DWORD cb = 0;
35
36 if (!::CertGetCertificateContextProperty(pCertContext, dwProperty, NULL, &cb))
37 {
38 CertExitWithLastError(hr, "Failed to get size of certificate property.");
39 }
40
41 pv = MemAlloc(cb, TRUE);
42 CertExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for certificate property.");
43
44 if (!::CertGetCertificateContextProperty(pCertContext, dwProperty, pv, &cb))
45 {
46 CertExitWithLastError(hr, "Failed to get certificate property.");
47 }
48
49 *ppvValue = pv;
50 pv = NULL;
51
52 if (pcbValue)
53 {
54 *pcbValue = cb;
55 }
56
57LExit:
58 ReleaseMem(pv);
59 return hr;
60}
61
62
63extern "C" HRESULT DAPI CertGetAuthenticodeSigningTimestamp(
64 __in CMSG_SIGNER_INFO* pSignerInfo,
65 __out FILETIME* pftSigningTimestamp
66 )
67{
68 HRESULT hr = S_OK;
69 CRYPT_INTEGER_BLOB* pBlob = NULL;
70 PCMSG_SIGNER_INFO pCounterSignerInfo = NULL;
71 DWORD cbSigningTimestamp = sizeof(FILETIME);
72
73 // Find the countersigner blob. The countersigner in Authenticode contains the time
74 // that signing took place. It's a "countersigner" because the signing time was sent
75 // off to the certificate authority in the sky to return the verified time signed.
76 for (DWORD i = 0; i < pSignerInfo->UnauthAttrs.cAttr; ++i)
77 {
78 if (CSTR_EQUAL == ::CompareStringA(LOCALE_NEUTRAL, 0, szOID_RSA_counterSign, -1, pSignerInfo->UnauthAttrs.rgAttr[i].pszObjId, -1))
79 {
80 pBlob = pSignerInfo->UnauthAttrs.rgAttr[i].rgValue;
81 break;
82 }
83 }
84
85 if (!pBlob)
86 {
87 hr = TRUST_E_FAIL;
88 CertExitOnFailure(hr, "Failed to find countersigner in signer information.");
89 }
90
91 hr = CrypDecodeObject(PKCS7_SIGNER_INFO, pBlob->pbData, pBlob->cbData, 0, reinterpret_cast<LPVOID*>(&pCounterSignerInfo), NULL);
92 CertExitOnFailure(hr, "Failed to decode countersigner information.");
93
94 pBlob = NULL; // reset the blob before searching for the signing time.
95
96 // Find the signing time blob in the countersigner.
97 for (DWORD i = 0; i < pCounterSignerInfo->AuthAttrs.cAttr; ++i)
98 {
99 if (CSTR_EQUAL == ::CompareStringA(LOCALE_NEUTRAL, 0, szOID_RSA_signingTime, -1, pCounterSignerInfo->AuthAttrs.rgAttr[i].pszObjId, -1))
100 {
101 pBlob = pCounterSignerInfo->AuthAttrs.rgAttr[i].rgValue;
102 break;
103 }
104 }
105
106 if (!pBlob)
107 {
108 hr = TRUST_E_FAIL;
109 CertExitOnFailure(hr, "Failed to find signing time in countersigner information.");
110 }
111
112 if (!::CryptDecodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, szOID_RSA_signingTime, pBlob->pbData, pBlob->cbData, 0, pftSigningTimestamp, &cbSigningTimestamp))
113 {
114 CertExitWithLastError(hr, "Failed to decode countersigner signing timestamp.");
115 }
116
117LExit:
118 ReleaseMem(pCounterSignerInfo);
119
120 return hr;
121}
122
123
124extern "C" HRESULT DAPI GetCryptProvFromCert(
125 __in_opt HWND hwnd,
126 __in PCCERT_CONTEXT pCert,
127 __out HCRYPTPROV *phCryptProv,
128 __out DWORD *pdwKeySpec,
129 __in BOOL *pfDidCryptAcquire,
130 __deref_opt_out LPWSTR *ppwszTmpContainer,
131 __deref_opt_out LPWSTR *ppwszProviderName,
132 __out DWORD *pdwProviderType
133 )
134{
135 HRESULT hr = S_OK;
136 HMODULE hMsSign32 = NULL;
137
138 typedef BOOL (WINAPI *GETCRYPTPROVFROMCERTPTR)(HWND, PCCERT_CONTEXT, HCRYPTPROV*, DWORD*,BOOL*,LPWSTR*,LPWSTR*,DWORD*);
139 GETCRYPTPROVFROMCERTPTR pGetCryptProvFromCert = NULL;
140
141 hr = LoadSystemLibrary(L"MsSign32.dll", &hMsSign32);
142 CertExitOnFailure(hr, "Failed to get handle to MsSign32.dll");
143
144 pGetCryptProvFromCert = (GETCRYPTPROVFROMCERTPTR)::GetProcAddress(hMsSign32, "GetCryptProvFromCert");
145 CertExitOnNullWithLastError(hMsSign32, hr, "Failed to get handle to MsSign32.dll");
146
147 if (!pGetCryptProvFromCert(hwnd,
148 pCert,
149 phCryptProv,
150 pdwKeySpec,
151 pfDidCryptAcquire,
152 ppwszTmpContainer,
153 ppwszProviderName,
154 pdwProviderType))
155 {
156 CertExitWithLastError(hr, "Failed to get CSP from cert.");
157 }
158LExit:
159 return hr;
160}
161
162extern "C" HRESULT DAPI FreeCryptProvFromCert(
163 __in BOOL fAcquired,
164 __in HCRYPTPROV hProv,
165 __in_opt LPWSTR pwszCapiProvider,
166 __in DWORD dwProviderType,
167 __in_opt LPWSTR pwszTmpContainer
168 )
169{
170 HRESULT hr = S_OK;
171 HMODULE hMsSign32 = NULL;
172
173 typedef void (WINAPI *FREECRYPTPROVFROMCERT)(BOOL, HCRYPTPROV, LPWSTR, DWORD, LPWSTR);
174 FREECRYPTPROVFROMCERT pFreeCryptProvFromCert = NULL;
175
176 hr = LoadSystemLibrary(L"MsSign32.dll", &hMsSign32);
177 CertExitOnFailure(hr, "Failed to get handle to MsSign32.dll");
178
179 pFreeCryptProvFromCert = (FREECRYPTPROVFROMCERT)::GetProcAddress(hMsSign32, "FreeCryptProvFromCert");
180 CertExitOnNullWithLastError(hMsSign32, hr, "Failed to get handle to MsSign32.dll");
181
182 pFreeCryptProvFromCert(fAcquired, hProv, pwszCapiProvider, dwProviderType, pwszTmpContainer);
183LExit:
184 return hr;
185}
186
187extern "C" HRESULT DAPI GetProvSecurityDesc(
188 __in HCRYPTPROV hProv,
189 __deref_out SECURITY_DESCRIPTOR** ppSecurity)
190{
191 HRESULT hr = S_OK;
192 ULONG ulSize = 0;
193 SECURITY_DESCRIPTOR* pSecurity = NULL;
194
195 // Get the size of the security descriptor.
196 if (!::CryptGetProvParam(
197 hProv,
198 PP_KEYSET_SEC_DESCR,
199 NULL,
200 &ulSize,
201 DACL_SECURITY_INFORMATION))
202 {
203 CertExitWithLastError(hr, "Error getting security descriptor size for CSP.");
204 }
205
206 // Allocate the memory for the security descriptor.
207 pSecurity = static_cast<SECURITY_DESCRIPTOR *>(MemAlloc(ulSize, TRUE));
208 CertExitOnNullWithLastError(pSecurity, hr, "Error allocating memory for CSP DACL");
209
210 // Get the security descriptor.
211 if (!::CryptGetProvParam(
212 hProv,
213 PP_KEYSET_SEC_DESCR,
214 (BYTE*)pSecurity,
215 &ulSize,
216 DACL_SECURITY_INFORMATION))
217 {
218 MemFree(pSecurity);
219 CertExitWithLastError(hr, "Error getting security descriptor for CSP.");
220 }
221 *ppSecurity = pSecurity;
222
223LExit:
224 return hr;
225}
226
227
228extern "C" HRESULT DAPI SetProvSecurityDesc(
229 __in HCRYPTPROV hProv,
230 __in SECURITY_DESCRIPTOR* pSecurity)
231{
232 HRESULT hr = S_OK;
233
234 // Set the new security descriptor.
235 if (!::CryptSetProvParam(
236 hProv,
237 PP_KEYSET_SEC_DESCR,
238 (BYTE*)pSecurity,
239 DACL_SECURITY_INFORMATION))
240 {
241 CertExitWithLastError(hr, "Error setting security descriptor for CSP.");
242 }
243LExit:
244 return hr;
245}
246
247extern "C" BOOL DAPI CertHasPrivateKey(
248 __in PCCERT_CONTEXT pCertContext,
249 __out_opt DWORD* pdwKeySpec)
250{
251 HCRYPTPROV_OR_NCRYPT_KEY_HANDLE hPrivateKey = NULL;
252 DWORD dwKeySpec = 0;
253 // set CRYPT_ACQUIRE_CACHE_FLAG so that we don't have to release the private key handle
254 BOOL fResult = ::CryptAcquireCertificatePrivateKey(
255 pCertContext,
256 CRYPT_ACQUIRE_SILENT_FLAG | CRYPT_ACQUIRE_CACHE_FLAG,
257 0, //pvReserved
258 &hPrivateKey,
259 &dwKeySpec,
260 NULL
261 );
262 if (pdwKeySpec)
263 {
264 *pdwKeySpec = dwKeySpec;
265 }
266 return fResult;
267}
268
269
270extern "C" HRESULT DAPI CertInstallSingleCertificate(
271 __in HCERTSTORE hStore,
272 __in PCCERT_CONTEXT pCertContext,
273 __in LPCWSTR wzName
274 )
275{
276 HRESULT hr = S_OK;
277 CERT_BLOB blob = { };
278
279 DWORD dwKeySpec = 0;
280
281 HCRYPTPROV hCsp = NULL;
282 LPWSTR pwszTmpContainer = NULL;
283 LPWSTR pwszProviderName = NULL;
284 DWORD dwProviderType = 0;
285 BOOL fAcquired = TRUE;
286
287 SECURITY_DESCRIPTOR* pSecurity = NULL;
288 SECURITY_DESCRIPTOR* pSecurityNew = NULL;
289
290 // Update the friendly name of the certificate to be configured.
291 blob.pbData = (BYTE*)wzName;
292 blob.cbData = (lstrlenW(wzName) + 1) * sizeof(WCHAR); // including terminating null
293
294 if (!::CertSetCertificateContextProperty(pCertContext, CERT_FRIENDLY_NAME_PROP_ID, 0, &blob))
295 {
296 CertExitWithLastError(hr, "Failed to set the friendly name of the certificate: %ls", wzName);
297 }
298
299 if (!::CertAddCertificateContextToStore(hStore, pCertContext, CERT_STORE_ADD_REPLACE_EXISTING, NULL))
300 {
301 CertExitWithLastError(hr, "Failed to add certificate to the store.");
302 }
303
304 // if the certificate has a private key, grant Administrators access
305 if (CertHasPrivateKey(pCertContext, &dwKeySpec))
306 {
307 if (AT_KEYEXCHANGE == dwKeySpec || AT_SIGNATURE == dwKeySpec)
308 {
309 // We added a CSP key
310 hr = GetCryptProvFromCert(NULL, pCertContext, &hCsp, &dwKeySpec, &fAcquired, &pwszTmpContainer, &pwszProviderName, &dwProviderType);
311 CertExitOnFailure(hr, "Failed to get handle to CSP");
312
313 hr = GetProvSecurityDesc(hCsp, &pSecurity);
314 CertExitOnFailure(hr, "Failed to get security descriptor of CSP");
315
316 hr = AclAddAdminToSecurityDescriptor(pSecurity, &pSecurityNew);
317 CertExitOnFailure(hr, "Failed to create new security descriptor");
318
319 hr = SetProvSecurityDesc(hCsp, pSecurityNew);
320 CertExitOnFailure(hr, "Failed to set Admin ACL on CSP");
321 }
322
323 if (CERT_NCRYPT_KEY_SPEC == dwKeySpec)
324 {
325 // We added a CNG key
326 // TODO change ACL on CNG key
327 }
328 }
329LExit:
330 if (hCsp)
331 {
332 FreeCryptProvFromCert(fAcquired, hCsp, NULL, dwProviderType, NULL);
333 }
334
335 ReleaseMem(pSecurity);
336
337 if (pSecurityNew)
338 {
339 AclFreeSecurityDescriptor(pSecurityNew);
340 }
341 return hr;
342}
diff --git a/src/libs/dutil/WixToolset.DUtil/conutil.cpp b/src/libs/dutil/WixToolset.DUtil/conutil.cpp
new file mode 100644
index 00000000..33e1b59a
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/conutil.cpp
@@ -0,0 +1,673 @@
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// Exit macros
7#define ConExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_CONUTIL, x, s, __VA_ARGS__)
8#define ConExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_CONUTIL, x, s, __VA_ARGS__)
9#define ConExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_CONUTIL, x, s, __VA_ARGS__)
10#define ConExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_CONUTIL, x, s, __VA_ARGS__)
11#define ConExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_CONUTIL, x, s, __VA_ARGS__)
12#define ConExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_CONUTIL, x, s, __VA_ARGS__)
13#define ConExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_CONUTIL, p, x, e, s, __VA_ARGS__)
14#define ConExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_CONUTIL, p, x, s, __VA_ARGS__)
15#define ConExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_CONUTIL, p, x, e, s, __VA_ARGS__)
16#define ConExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_CONUTIL, p, x, s, __VA_ARGS__)
17#define ConExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_CONUTIL, e, x, s, __VA_ARGS__)
18#define ConExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_CONUTIL, g, x, s, __VA_ARGS__)
19
20
21static HANDLE vhStdIn = INVALID_HANDLE_VALUE;
22static HANDLE vhStdOut = INVALID_HANDLE_VALUE;
23static BOOL vfConsoleIn = FALSE;
24static BOOL vfConsoleOut = FALSE;
25static CONSOLE_SCREEN_BUFFER_INFO vcsbiInfo;
26
27
28extern "C" HRESULT DAPI ConsoleInitialize()
29{
30 Assert(INVALID_HANDLE_VALUE == vhStdOut);
31 HRESULT hr = S_OK;
32 UINT er;
33
34 vhStdIn = ::GetStdHandle(STD_INPUT_HANDLE);
35 if (INVALID_HANDLE_VALUE == vhStdIn)
36 {
37 ConExitOnLastError(hr, "failed to open stdin");
38 }
39
40 vhStdOut = ::GetStdHandle(STD_OUTPUT_HANDLE);
41 if (INVALID_HANDLE_VALUE == vhStdOut)
42 {
43 ConExitOnLastError(hr, "failed to open stdout");
44 }
45
46 // check if we have a std in on the console
47 if (::GetConsoleScreenBufferInfo(vhStdIn, &vcsbiInfo))
48 {
49 vfConsoleIn = TRUE;
50 }
51 else
52 {
53 er = ::GetLastError();
54 if (ERROR_INVALID_HANDLE == er)
55 {
56 vfConsoleIn= FALSE;
57 hr = S_OK;
58 }
59 else
60 {
61 ConExitOnWin32Error(er, hr, "failed to get input console screen buffer info");
62 }
63 }
64
65 if (::GetConsoleScreenBufferInfo(vhStdOut, &vcsbiInfo))
66 {
67 vfConsoleOut = TRUE;
68 }
69 else // no console
70 {
71 memset(&vcsbiInfo, 0, sizeof(vcsbiInfo));
72 er = ::GetLastError();
73 if (ERROR_INVALID_HANDLE == er)
74 {
75 vfConsoleOut = FALSE;
76 hr = S_OK;
77 }
78 else
79 {
80 ConExitOnWin32Error(er, hr, "failed to get output console screen buffer info");
81 }
82 }
83
84LExit:
85 if (FAILED(hr))
86 {
87 if (INVALID_HANDLE_VALUE != vhStdOut)
88 {
89 ::CloseHandle(vhStdOut);
90 }
91
92 if (INVALID_HANDLE_VALUE != vhStdIn && vhStdOut != vhStdIn)
93 {
94 ::CloseHandle(vhStdIn);
95 }
96
97 vhStdOut = INVALID_HANDLE_VALUE;
98 vhStdIn = INVALID_HANDLE_VALUE;
99 }
100
101 return hr;
102}
103
104
105extern "C" void DAPI ConsoleUninitialize()
106{
107 BOOL fOutEqualsIn = vhStdOut == vhStdIn;
108
109 memset(&vcsbiInfo, 0, sizeof(vcsbiInfo));
110
111 if (INVALID_HANDLE_VALUE != vhStdOut)
112 {
113 ::CloseHandle(vhStdOut);
114 }
115
116 if (INVALID_HANDLE_VALUE != vhStdIn && !fOutEqualsIn)
117 {
118 ::CloseHandle(vhStdIn);
119 }
120
121 vhStdOut = INVALID_HANDLE_VALUE;
122 vhStdIn = INVALID_HANDLE_VALUE;
123}
124
125
126extern "C" void DAPI ConsoleGreen()
127{
128 AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called");
129 if (vfConsoleOut)
130 {
131 ::SetConsoleTextAttribute(vhStdOut, FOREGROUND_GREEN | FOREGROUND_INTENSITY);
132 }
133}
134
135
136extern "C" void DAPI ConsoleRed()
137{
138 AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called");
139 if (vfConsoleOut)
140 {
141 ::SetConsoleTextAttribute(vhStdOut, FOREGROUND_RED | FOREGROUND_INTENSITY);
142 }
143}
144
145
146extern "C" void DAPI ConsoleYellow()
147{
148 AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called");
149 if (vfConsoleOut)
150 {
151 ::SetConsoleTextAttribute(vhStdOut, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY);
152 }
153}
154
155
156extern "C" void DAPI ConsoleNormal()
157{
158 AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called");
159 if (vfConsoleOut)
160 {
161 ::SetConsoleTextAttribute(vhStdOut, vcsbiInfo.wAttributes);
162 }
163}
164
165
166/********************************************************************
167 ConsoleWrite - full color printfA without libc
168
169 NOTE: use FormatMessage formatting ("%1" or "%1!d!") not plain printf formatting ("%ls" or "%d")
170 assumes already in normal color and resets the screen to normal color
171********************************************************************/
172extern "C" HRESULT DAPI ConsoleWrite(
173 CONSOLE_COLOR cc,
174 __in_z __format_string LPCSTR szFormat,
175 ...
176 )
177{
178 AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called");
179 HRESULT hr = S_OK;
180 LPSTR pszOutput = NULL;
181 DWORD cchOutput = 0;
182 DWORD cbWrote = 0;
183 DWORD cbTotal = 0;
184
185 // set the color
186 switch (cc)
187 {
188 case CONSOLE_COLOR_NORMAL: break; // do nothing
189 case CONSOLE_COLOR_RED: ConsoleRed(); break;
190 case CONSOLE_COLOR_YELLOW: ConsoleYellow(); break;
191 case CONSOLE_COLOR_GREEN: ConsoleGreen(); break;
192 }
193
194 va_list args;
195 va_start(args, szFormat);
196 hr = StrAnsiAllocFormattedArgs(&pszOutput, szFormat, args);
197 va_end(args);
198 ConExitOnFailure(hr, "failed to format message: \"%s\"", szFormat);
199
200 cchOutput = lstrlenA(pszOutput);
201 while (cbTotal < (sizeof(*pszOutput) * cchOutput))
202 {
203 if (!::WriteFile(vhStdOut, reinterpret_cast<BYTE*>(pszOutput) + cbTotal, cchOutput * sizeof(*pszOutput) - cbTotal, &cbWrote, NULL))
204 {
205 ConExitOnLastError(hr, "failed to write output to console: %s", pszOutput);
206 }
207
208 cbTotal += cbWrote;
209 }
210
211 // reset the color to normal
212 if (CONSOLE_COLOR_NORMAL != cc)
213 {
214 ConsoleNormal();
215 }
216
217LExit:
218 ReleaseStr(pszOutput);
219 return hr;
220}
221
222
223/********************************************************************
224 ConsoleWriteLine - full color printfA plus newline without libc
225
226 NOTE: use FormatMessage formatting ("%1" or "%1!d!") not plain printf formatting ("%ls" or "%d")
227 assumes already in normal color and resets the screen to normal color
228********************************************************************/
229extern "C" HRESULT DAPI ConsoleWriteLine(
230 CONSOLE_COLOR cc,
231 __in_z __format_string LPCSTR szFormat,
232 ...
233 )
234{
235 AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called");
236 HRESULT hr = S_OK;
237 LPSTR pszOutput = NULL;
238 DWORD cchOutput = 0;
239 DWORD cbWrote = 0;
240 DWORD cbTotal = 0;
241 LPCSTR szNewLine = "\r\n";
242
243 // set the color
244 switch (cc)
245 {
246 case CONSOLE_COLOR_NORMAL: break; // do nothing
247 case CONSOLE_COLOR_RED: ConsoleRed(); break;
248 case CONSOLE_COLOR_YELLOW: ConsoleYellow(); break;
249 case CONSOLE_COLOR_GREEN: ConsoleGreen(); break;
250 }
251
252 va_list args;
253 va_start(args, szFormat);
254 hr = StrAnsiAllocFormattedArgs(&pszOutput, szFormat, args);
255 va_end(args);
256 ConExitOnFailure(hr, "failed to format message: \"%s\"", szFormat);
257
258 //
259 // write the string
260 //
261 cchOutput = lstrlenA(pszOutput);
262 while (cbTotal < (sizeof(*pszOutput) * cchOutput))
263 {
264 if (!::WriteFile(vhStdOut, reinterpret_cast<BYTE*>(pszOutput) + cbTotal, cchOutput * sizeof(*pszOutput) - cbTotal, &cbWrote, NULL))
265 ConExitOnLastError(hr, "failed to write output to console: %s", pszOutput);
266
267 cbTotal += cbWrote;
268 }
269
270 //
271 // write the newline
272 //
273 if (!::WriteFile(vhStdOut, reinterpret_cast<const BYTE*>(szNewLine), 2, &cbWrote, NULL))
274 {
275 ConExitOnLastError(hr, "failed to write newline to console");
276 }
277
278 // reset the color to normal
279 if (CONSOLE_COLOR_NORMAL != cc)
280 {
281 ConsoleNormal();
282 }
283
284LExit:
285 ReleaseStr(pszOutput);
286 return hr;
287}
288
289
290/********************************************************************
291 ConsoleWriteError - display an error to the screen
292
293 NOTE: use FormatMessage formatting ("%1" or "%1!d!") not plain printf formatting ("%s" or "%d")
294********************************************************************/
295HRESULT ConsoleWriteError(
296 HRESULT hrError,
297 CONSOLE_COLOR cc,
298 __in_z __format_string LPCSTR szFormat,
299 ...
300 )
301{
302 HRESULT hr = S_OK;
303 LPSTR pszMessage = NULL;
304
305 va_list args;
306 va_start(args, szFormat);
307 hr = StrAnsiAllocFormattedArgs(&pszMessage, szFormat, args);
308 va_end(args);
309 ConExitOnFailure(hr, "failed to format error message: \"%s\"", szFormat);
310
311 if (FAILED(hrError))
312 {
313 hr = ConsoleWriteLine(cc, "Error 0x%x: %s", hrError, pszMessage);
314 }
315 else
316 {
317 hr = ConsoleWriteLine(cc, "Error: %s", pszMessage);
318 }
319
320LExit:
321 ReleaseStr(pszMessage);
322 return hr;
323}
324
325
326/********************************************************************
327 ConsoleReadW - get console input without libc
328
329 NOTE: only supports reading ANSI characters
330********************************************************************/
331extern "C" HRESULT DAPI ConsoleReadW(
332 __deref_out_z LPWSTR* ppwzBuffer
333 )
334{
335 AssertSz(INVALID_HANDLE_VALUE != vhStdIn, "ConsoleInitialize() has not been called");
336 Assert(ppwzBuffer);
337
338 HRESULT hr = S_OK;
339 LPSTR psz = NULL;
340 DWORD cch = 0;
341 DWORD cchRead = 0;
342 DWORD cchTotalRead = 0;
343
344 cch = 64;
345 hr = StrAnsiAlloc(&psz, cch);
346 ConExitOnFailure(hr, "failed to allocate memory to read from console");
347
348 // loop until we read the \r\n from the console
349 for (;;)
350 {
351 // read one character at a time, since that seems to be the only way to make this work
352 if (!::ReadFile(vhStdIn, psz + cchTotalRead, 1, &cchRead, NULL))
353 ConExitOnLastError(hr, "failed to read string from console");
354
355 cchTotalRead += cchRead;
356 if (1 < cchTotalRead && '\r' == psz[cchTotalRead - 2] || '\n' == psz[cchTotalRead - 1])
357 {
358 psz[cchTotalRead - 2] = '\0'; // chop off the \r\n
359 break;
360 }
361 else if (0 == cchRead) // nothing more was read
362 {
363 psz[cchTotalRead] = '\0'; // null termintate and bail
364 break;
365 }
366
367 if (cchTotalRead == cch)
368 {
369 cch *= 2; // double everytime we run out of space
370 hr = StrAnsiAlloc(&psz, cch);
371 ConExitOnFailure(hr, "failed to allocate memory to read from console");
372 }
373 }
374
375 hr = StrAllocStringAnsi(ppwzBuffer, psz, 0, CP_ACP);
376
377LExit:
378 ReleaseStr(psz);
379 return hr;
380}
381
382
383/********************************************************************
384 ConsoleReadNonBlockingW - Read from the console without blocking
385 Won't work for redirected files (exe < txtfile), but will work for stdin redirected to
386 an anonymous or named pipe
387
388 if (fReadLine), stop reading immediately when \r\n is found
389*********************************************************************/
390extern "C" HRESULT DAPI ConsoleReadNonBlockingW(
391 __deref_out_ecount_opt(*pcchSize) LPWSTR* ppwzBuffer,
392 __out DWORD* pcchSize,
393 BOOL fReadLine
394 )
395{
396 Assert(INVALID_HANDLE_VALUE != vhStdIn && pcchSize);
397 HRESULT hr = S_OK;
398
399 LPSTR psz = NULL;
400
401 ConExitOnNull(ppwzBuffer, hr, E_INVALIDARG, "Failed to read from console because buffer was not provided");
402
403 DWORD dwRead;
404 DWORD dwNumInput;
405
406 DWORD cchTotal = 0;
407 DWORD cch = 8;
408
409 DWORD cchRead = 0;
410 DWORD cchTotalRead = 0;
411
412 DWORD dwIndex = 0;
413 DWORD er;
414
415 INPUT_RECORD ir;
416 WCHAR chIn;
417
418 *ppwzBuffer = NULL;
419 *pcchSize = 0;
420
421 // If we really have a handle to stdin, and not the end of a pipe
422 if (!PeekNamedPipe(vhStdIn, NULL, 0, NULL, &dwRead, NULL))
423 {
424 er = ::GetLastError();
425 if (ERROR_INVALID_HANDLE != er)
426 {
427 ExitFunction1(hr = HRESULT_FROM_WIN32(er));
428 }
429
430 if (!GetNumberOfConsoleInputEvents(vhStdIn, &dwRead))
431 {
432 ConExitOnLastError(hr, "failed to peek at console input");
433 }
434
435 if (0 == dwRead)
436 {
437 ExitFunction1(hr = S_FALSE);
438 }
439
440 for (/* dwRead from num of input events */; dwRead > 0; dwRead--)
441 {
442 if (!ReadConsoleInputW(vhStdIn, &ir, 1, &dwNumInput))
443 {
444 ConExitOnLastError(hr, "Failed to read input from console");
445 }
446
447 // If what we have is a KEY_EVENT, and that event signifies keyUp, we're interested
448 if (KEY_EVENT == ir.EventType && FALSE == ir.Event.KeyEvent.bKeyDown)
449 {
450 chIn = ir.Event.KeyEvent.uChar.UnicodeChar;
451
452 if (0 == cchTotal)
453 {
454 cchTotal = cch;
455 cch *= 2;
456 StrAlloc(ppwzBuffer, cch);
457 }
458
459 (*ppwzBuffer)[dwIndex] = chIn;
460
461 if (fReadLine && (L'\r' == (*ppwzBuffer)[dwIndex - 1] && L'\n' == (*ppwzBuffer)[dwIndex]))
462 {
463 *ppwzBuffer[dwIndex - 1] = L'\0';
464 dwIndex -= 1;
465 break;
466 }
467
468 ++dwIndex;
469 cchTotal--;
470 }
471 }
472
473 *pcchSize = dwIndex;
474 }
475 else
476 {
477 // otherwise, the peek worked, and we have the end of a pipe
478 if (0 == dwRead)
479 ExitFunction1(hr = S_FALSE);
480
481 cch = 8;
482 hr = StrAnsiAlloc(&psz, cch);
483 ConExitOnFailure(hr, "failed to allocate memory to read from console");
484
485 for (/*dwRead from PeekNamedPipe*/; dwRead > 0; dwRead--)
486 {
487 // read one character at a time, since that seems to be the only way to make this work
488 if (!::ReadFile(vhStdIn, psz + cchTotalRead, 1, &cchRead, NULL))
489 {
490 ConExitOnLastError(hr, "failed to read string from console");
491 }
492
493 cchTotalRead += cchRead;
494 if (fReadLine && '\r' == psz[cchTotalRead - 1] && '\n' == psz[cchTotalRead])
495 {
496 psz[cchTotalRead - 1] = '\0'; // chop off the \r\n
497 cchTotalRead -= 1;
498 break;
499 }
500 else if (0 == cchRead) // nothing more was read
501 {
502 psz[cchTotalRead] = '\0'; // null termintate and bail
503 break;
504 }
505
506 if (cchTotalRead == cch)
507 {
508 cch *= 2; // double everytime we run out of space
509 hr = StrAnsiAlloc(&psz, cch);
510 ConExitOnFailure(hr, "failed to allocate memory to read from console");
511 }
512 }
513
514 *pcchSize = cchTotalRead;
515 hr = StrAllocStringAnsi(ppwzBuffer, psz, cchTotalRead, CP_ACP);
516 }
517
518LExit:
519 ReleaseStr(psz);
520
521 return hr;
522}
523
524
525/********************************************************************
526 ConsoleReadStringA - get console input without libc
527
528*********************************************************************/
529extern "C" HRESULT DAPI ConsoleReadStringA(
530 __deref_inout_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPSTR* ppszCharBuffer,
531 CONST DWORD cchCharBuffer,
532 __out DWORD* pcchNumCharReturn
533 )
534{
535 AssertSz(INVALID_HANDLE_VALUE != vhStdIn, "ConsoleInitialize() has not been called");
536 HRESULT hr = S_OK;
537 if (ppszCharBuffer && (pcchNumCharReturn || cchCharBuffer < 2))
538 {
539 DWORD iRead = 1;
540 DWORD iReadCharTotal = 0;
541 if (ppszCharBuffer && *ppszCharBuffer == NULL)
542 {
543 do
544 {
545 hr = StrAnsiAlloc(ppszCharBuffer, cchCharBuffer * iRead);
546 ConExitOnFailure(hr, "failed to allocate memory for ConsoleReadStringW");
547 // ReadConsoleW will not return until <Return>, the last two chars are 13 and 10.
548 if (!::ReadConsoleA(vhStdIn, *ppszCharBuffer + iReadCharTotal, cchCharBuffer, pcchNumCharReturn, NULL) || *pcchNumCharReturn == 0)
549 {
550 ConExitOnLastError(hr, "failed to read string from console");
551 }
552 iReadCharTotal += *pcchNumCharReturn;
553 iRead += 1;
554 }
555 while((*ppszCharBuffer)[iReadCharTotal - 1] != 10 || (*ppszCharBuffer)[iReadCharTotal - 2] != 13);
556 *pcchNumCharReturn = iReadCharTotal;
557 }
558 else
559 {
560 if (!::ReadConsoleA(vhStdIn, *ppszCharBuffer, cchCharBuffer, pcchNumCharReturn, NULL) ||
561 *pcchNumCharReturn > cchCharBuffer || *pcchNumCharReturn == 0)
562 {
563 ConExitOnLastError(hr, "failed to read string from console");
564 }
565 if ((*ppszCharBuffer)[*pcchNumCharReturn - 1] != 10 ||
566 (*ppszCharBuffer)[*pcchNumCharReturn - 2] != 13)
567 {
568 // need read more
569 hr = ERROR_MORE_DATA;
570 }
571 }
572 }
573 else
574 {
575 hr = E_INVALIDARG;
576 }
577
578LExit:
579 return hr;
580}
581
582/********************************************************************
583 ConsoleReadStringW - get console input without libc
584
585*********************************************************************/
586extern "C" HRESULT DAPI ConsoleReadStringW(
587 __deref_inout_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPWSTR* ppwzCharBuffer,
588 const DWORD cchCharBuffer,
589 __out DWORD* pcchNumCharReturn
590 )
591{
592 AssertSz(INVALID_HANDLE_VALUE != vhStdIn, "ConsoleInitialize() has not been called");
593 HRESULT hr = S_OK;
594 if (ppwzCharBuffer && (pcchNumCharReturn || cchCharBuffer < 2))
595 {
596 DWORD iRead = 1;
597 DWORD iReadCharTotal = 0;
598 if (*ppwzCharBuffer == NULL)
599 {
600 do
601 {
602 hr = StrAlloc(ppwzCharBuffer, cchCharBuffer * iRead);
603 ConExitOnFailure(hr, "failed to allocate memory for ConsoleReadStringW");
604 // ReadConsoleW will not return until <Return>, the last two chars are 13 and 10.
605 if (!::ReadConsoleW(vhStdIn, *ppwzCharBuffer + iReadCharTotal, cchCharBuffer, pcchNumCharReturn, NULL) || *pcchNumCharReturn == 0)
606 {
607 ConExitOnLastError(hr, "failed to read string from console");
608 }
609 iReadCharTotal += *pcchNumCharReturn;
610 iRead += 1;
611 }
612 while((*ppwzCharBuffer)[iReadCharTotal - 1] != 10 || (*ppwzCharBuffer)[iReadCharTotal - 2] != 13);
613 *pcchNumCharReturn = iReadCharTotal;
614 }
615 else
616 {
617 if (!::ReadConsoleW(vhStdIn, *ppwzCharBuffer, cchCharBuffer, pcchNumCharReturn, NULL) ||
618 *pcchNumCharReturn > cchCharBuffer || *pcchNumCharReturn == 0)
619 {
620 ConExitOnLastError(hr, "failed to read string from console");
621 }
622 if ((*ppwzCharBuffer)[*pcchNumCharReturn - 1] != 10 ||
623 (*ppwzCharBuffer)[*pcchNumCharReturn - 2] != 13)
624 {
625 // need read more
626 hr = ERROR_MORE_DATA;
627 }
628 }
629 }
630 else
631 {
632 hr = E_INVALIDARG;
633 }
634
635LExit:
636 return hr;
637}
638
639/********************************************************************
640 ConsoleSetReadHidden - set console input no echo
641
642*********************************************************************/
643extern "C" HRESULT DAPI ConsoleSetReadHidden(void)
644{
645 AssertSz(INVALID_HANDLE_VALUE != vhStdIn, "ConsoleInitialize() has not been called");
646 HRESULT hr = S_OK;
647 ::FlushConsoleInputBuffer(vhStdIn);
648 if (!::SetConsoleMode(vhStdIn, ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT))
649 {
650 ConExitOnLastError(hr, "failed to set console input mode to be hidden");
651 }
652
653LExit:
654 return hr;
655}
656
657/********************************************************************
658 ConsoleSetReadNormal - reset to echo
659
660*********************************************************************/
661extern "C" HRESULT DAPI ConsoleSetReadNormal(void)
662{
663 AssertSz(INVALID_HANDLE_VALUE != vhStdIn, "ConsoleInitialize() has not been called");
664 HRESULT hr = S_OK;
665 if (!::SetConsoleMode(vhStdIn, ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_MOUSE_INPUT))
666 {
667 ConExitOnLastError(hr, "failed to set console input mode to be normal");
668 }
669
670LExit:
671 return hr;
672}
673
diff --git a/src/libs/dutil/WixToolset.DUtil/cryputil.cpp b/src/libs/dutil/WixToolset.DUtil/cryputil.cpp
new file mode 100644
index 00000000..24bb83f1
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/cryputil.cpp
@@ -0,0 +1,404 @@
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// Exit macros
7#define CrypExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_CRYPUTIL, x, s, __VA_ARGS__)
8#define CrypExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_CRYPUTIL, x, s, __VA_ARGS__)
9#define CrypExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_CRYPUTIL, x, s, __VA_ARGS__)
10#define CrypExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_CRYPUTIL, x, s, __VA_ARGS__)
11#define CrypExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_CRYPUTIL, x, s, __VA_ARGS__)
12#define CrypExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_CRYPUTIL, x, s, __VA_ARGS__)
13#define CrypExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_CRYPUTIL, p, x, e, s, __VA_ARGS__)
14#define CrypExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_CRYPUTIL, p, x, s, __VA_ARGS__)
15#define CrypExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_CRYPUTIL, p, x, e, s, __VA_ARGS__)
16#define CrypExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_CRYPUTIL, p, x, s, __VA_ARGS__)
17#define CrypExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_CRYPUTIL, e, x, s, __VA_ARGS__)
18#define CrypExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_CRYPUTIL, g, x, s, __VA_ARGS__)
19
20static PFN_RTLENCRYPTMEMORY vpfnRtlEncryptMemory = NULL;
21static PFN_RTLDECRYPTMEMORY vpfnRtlDecryptMemory = NULL;
22static PFN_CRYPTPROTECTMEMORY vpfnCryptProtectMemory = NULL;
23static PFN_CRYPTUNPROTECTMEMORY vpfnCryptUnprotectMemory = NULL;
24
25static HMODULE vhAdvApi32Dll = NULL;
26static HMODULE vhCrypt32Dll = NULL;
27static BOOL vfCrypInitialized = FALSE;
28
29// function definitions
30
31/********************************************************************
32 CrypInitialize - initializes cryputil
33
34*********************************************************************/
35extern "C" HRESULT DAPI CrypInitialize(
36 )
37{
38 HRESULT hr = S_OK;
39
40 hr = LoadSystemLibrary(L"AdvApi32.dll", &vhAdvApi32Dll);
41 if (SUCCEEDED(hr))
42 {
43 // Ignore failures - if these don't exist, we'll try the Crypt methods.
44 vpfnRtlEncryptMemory = reinterpret_cast<PFN_RTLENCRYPTMEMORY>(::GetProcAddress(vhAdvApi32Dll, "SystemFunction040"));
45 vpfnRtlDecryptMemory = reinterpret_cast<PFN_RTLDECRYPTMEMORY>(::GetProcAddress(vhAdvApi32Dll, "SystemFunction041"));
46 }
47 if (!vpfnRtlEncryptMemory || !vpfnRtlDecryptMemory)
48 {
49 hr = LoadSystemLibrary(L"Crypt32.dll", &vhCrypt32Dll);
50 CrypExitOnFailure(hr, "Failed to load Crypt32.dll");
51
52 vpfnCryptProtectMemory = reinterpret_cast<PFN_CRYPTPROTECTMEMORY>(::GetProcAddress(vhCrypt32Dll, "CryptProtectMemory"));
53 if (!vpfnRtlEncryptMemory && !vpfnCryptProtectMemory)
54 {
55 CrypExitWithLastError(hr, "Failed to load an encryption method");
56 }
57 vpfnCryptUnprotectMemory = reinterpret_cast<PFN_CRYPTUNPROTECTMEMORY>(::GetProcAddress(vhCrypt32Dll, "CryptUnprotectMemory"));
58 if (!vpfnRtlDecryptMemory && !vpfnCryptUnprotectMemory)
59 {
60 CrypExitWithLastError(hr, "Failed to load a decryption method");
61 }
62 }
63
64 vfCrypInitialized = TRUE;
65
66LExit:
67 return hr;
68}
69
70
71/********************************************************************
72 CrypUninitialize - uninitializes cryputil
73
74*********************************************************************/
75extern "C" void DAPI CrypUninitialize(
76 )
77{
78 if (vhAdvApi32Dll)
79 {
80 ::FreeLibrary(vhAdvApi32Dll);
81 vhAdvApi32Dll = NULL;
82 vpfnRtlEncryptMemory = NULL;
83 vpfnRtlDecryptMemory = NULL;
84 }
85
86 if (vhCrypt32Dll)
87 {
88 ::FreeLibrary(vhCrypt32Dll);
89 vhCrypt32Dll = NULL;
90 vpfnCryptProtectMemory = NULL;
91 vpfnCryptUnprotectMemory = NULL;
92 }
93
94 vfCrypInitialized = FALSE;
95}
96
97extern "C" HRESULT DAPI CrypDecodeObject(
98 __in_z LPCSTR szStructType,
99 __in_ecount(cbData) const BYTE* pbData,
100 __in DWORD cbData,
101 __in DWORD dwFlags,
102 __out LPVOID* ppvObject,
103 __out_opt DWORD* pcbObject
104 )
105{
106 HRESULT hr = S_OK;
107 LPVOID pvObject = NULL;
108 DWORD cbObject = 0;
109
110 if (!::CryptDecodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, szStructType, pbData, cbData, dwFlags, NULL, &cbObject))
111 {
112 CrypExitWithLastError(hr, "Failed to decode object to determine size.");
113 }
114
115 pvObject = MemAlloc(cbObject, TRUE);
116 CrypExitOnNull(pvObject, hr, E_OUTOFMEMORY, "Failed to allocate memory for decoded object.");
117
118 if (!::CryptDecodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, szStructType, pbData, cbData, dwFlags, pvObject, &cbObject))
119 {
120 CrypExitWithLastError(hr, "Failed to decode object.");
121 }
122
123 *ppvObject = pvObject;
124 pvObject = NULL;
125
126 if (pcbObject)
127 {
128 *pcbObject = cbObject;
129 }
130
131LExit:
132 ReleaseMem(pvObject);
133
134 return hr;
135}
136
137
138extern "C" HRESULT DAPI CrypMsgGetParam(
139 __in HCRYPTMSG hCryptMsg,
140 __in DWORD dwType,
141 __in DWORD dwIndex,
142 __out LPVOID* ppvData,
143 __out_opt DWORD* pcbData
144 )
145{
146 HRESULT hr = S_OK;
147 LPVOID pv = NULL;
148 DWORD cb = 0;
149
150 if (!::CryptMsgGetParam(hCryptMsg, dwType, dwIndex, NULL, &cb))
151 {
152 CrypExitWithLastError(hr, "Failed to get crypt message parameter data size.");
153 }
154
155 pv = MemAlloc(cb, TRUE);
156 CrypExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for crypt message parameter.");
157
158 if (!::CryptMsgGetParam(hCryptMsg, dwType, dwIndex, pv, &cb))
159 {
160 CrypExitWithLastError(hr, "Failed to get crypt message parameter.");
161 }
162
163 *ppvData = pv;
164 pv = NULL;
165
166 if (pcbData)
167 {
168 *pcbData = cb;
169 }
170
171LExit:
172 ReleaseMem(pv);
173
174 return hr;
175}
176
177
178extern "C" HRESULT DAPI CrypHashFile(
179 __in_z LPCWSTR wzFilePath,
180 __in DWORD dwProvType,
181 __in ALG_ID algid,
182 __out_bcount(cbHash) BYTE* pbHash,
183 __in DWORD cbHash,
184 __out_opt DWORD64* pqwBytesHashed
185 )
186{
187 HRESULT hr = S_OK;
188 HANDLE hFile = INVALID_HANDLE_VALUE;
189
190 // open input file
191 hFile = ::CreateFileW(wzFilePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
192 if (INVALID_HANDLE_VALUE == hFile)
193 {
194 CrypExitWithLastError(hr, "Failed to open input file.");
195 }
196
197 hr = CrypHashFileHandle(hFile, dwProvType, algid, pbHash, cbHash, pqwBytesHashed);
198 CrypExitOnFailure(hr, "Failed to hash file: %ls", wzFilePath);
199
200LExit:
201 ReleaseFileHandle(hFile);
202
203 return hr;
204}
205
206
207extern "C" HRESULT DAPI CrypHashFileHandle(
208 __in HANDLE hFile,
209 __in DWORD dwProvType,
210 __in ALG_ID algid,
211 __out_bcount(cbHash) BYTE* pbHash,
212 __in DWORD cbHash,
213 __out_opt DWORD64* pqwBytesHashed
214 )
215{
216 HRESULT hr = S_OK;
217 HCRYPTPROV hProv = NULL;
218 HCRYPTHASH hHash = NULL;
219 DWORD cbRead = 0;
220 BYTE rgbBuffer[4096] = { };
221 const LARGE_INTEGER liZero = { };
222
223 // get handle to the crypto provider
224 if (!::CryptAcquireContextW(&hProv, NULL, NULL, dwProvType, CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
225 {
226 CrypExitWithLastError(hr, "Failed to acquire crypto context.");
227 }
228
229 // initiate hash
230 if (!::CryptCreateHash(hProv, algid, 0, 0, &hHash))
231 {
232 CrypExitWithLastError(hr, "Failed to initiate hash.");
233 }
234
235 for (;;)
236 {
237 // read data block
238 if (!::ReadFile(hFile, rgbBuffer, sizeof(rgbBuffer), &cbRead, NULL))
239 {
240 CrypExitWithLastError(hr, "Failed to read data block.");
241 }
242
243 if (!cbRead)
244 {
245 break; // end of file
246 }
247
248 // hash data block
249 if (!::CryptHashData(hHash, rgbBuffer, cbRead, 0))
250 {
251 CrypExitWithLastError(hr, "Failed to hash data block.");
252 }
253 }
254
255 // get hash value
256 if (!::CryptGetHashParam(hHash, HP_HASHVAL, pbHash, &cbHash, 0))
257 {
258 CrypExitWithLastError(hr, "Failed to get hash value.");
259 }
260
261 if (pqwBytesHashed)
262 {
263 if (!::SetFilePointerEx(hFile, liZero, (LARGE_INTEGER*)pqwBytesHashed, FILE_CURRENT))
264 {
265 CrypExitWithLastError(hr, "Failed to get file pointer.");
266 }
267 }
268
269LExit:
270 if (hHash)
271 {
272 ::CryptDestroyHash(hHash);
273 }
274 if (hProv)
275 {
276 ::CryptReleaseContext(hProv, 0);
277 }
278
279 return hr;
280}
281
282HRESULT DAPI CrypHashBuffer(
283 __in_bcount(cbBuffer) const BYTE* pbBuffer,
284 __in SIZE_T cbBuffer,
285 __in DWORD dwProvType,
286 __in ALG_ID algid,
287 __out_bcount(cbHash) BYTE* pbHash,
288 __in DWORD cbHash
289 )
290{
291 HRESULT hr = S_OK;
292 HCRYPTPROV hProv = NULL;
293 HCRYPTHASH hHash = NULL;
294 DWORD cbDataHashed = 0;
295 SIZE_T cbTotal = 0;
296 SIZE_T cbRemaining = 0;
297
298 // get handle to the crypto provider
299 if (!::CryptAcquireContextW(&hProv, NULL, NULL, dwProvType, CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
300 {
301 CrypExitWithLastError(hr, "Failed to acquire crypto context.");
302 }
303
304 // initiate hash
305 if (!::CryptCreateHash(hProv, algid, 0, 0, &hHash))
306 {
307 CrypExitWithLastError(hr, "Failed to initiate hash.");
308 }
309
310 do
311 {
312 cbRemaining = cbBuffer - cbTotal;
313 cbDataHashed = (DWORD)min(DWORD_MAX, cbRemaining);
314 if (!::CryptHashData(hHash, pbBuffer + cbTotal, cbDataHashed, 0))
315 {
316 CrypExitWithLastError(hr, "Failed to hash data.");
317 }
318
319 cbTotal += cbDataHashed;
320 } while (cbTotal < cbBuffer);
321
322 // get hash value
323 if (!::CryptGetHashParam(hHash, HP_HASHVAL, pbHash, &cbHash, 0))
324 {
325 CrypExitWithLastError(hr, "Failed to get hash value.");
326 }
327
328LExit:
329 if (hHash)
330 {
331 ::CryptDestroyHash(hHash);
332 }
333 if (hProv)
334 {
335 ::CryptReleaseContext(hProv, 0);
336 }
337
338 return hr;
339}
340
341HRESULT DAPI CrypEncryptMemory(
342 __inout LPVOID pData,
343 __in DWORD cbData,
344 __in DWORD dwFlags
345 )
346{
347 HRESULT hr = E_FAIL;
348
349 if (0 != cbData % CRYP_ENCRYPT_MEMORY_SIZE)
350 {
351 hr = E_INVALIDARG;
352 }
353 else if (vpfnRtlEncryptMemory)
354 {
355 hr = static_cast<HRESULT>(vpfnRtlEncryptMemory(pData, cbData, dwFlags));
356 }
357 else if (vpfnCryptProtectMemory)
358 {
359 if (vpfnCryptProtectMemory(pData, cbData, dwFlags))
360 {
361 hr = S_OK;
362 }
363 else
364 {
365 hr = HRESULT_FROM_WIN32(::GetLastError());
366 }
367 }
368 CrypExitOnFailure(hr, "Failed to encrypt memory");
369LExit:
370 return hr;
371}
372
373HRESULT DAPI CrypDecryptMemory(
374 __inout LPVOID pData,
375 __in DWORD cbData,
376 __in DWORD dwFlags
377 )
378{
379 HRESULT hr = E_FAIL;
380
381 if (0 != cbData % CRYP_ENCRYPT_MEMORY_SIZE)
382 {
383 hr = E_INVALIDARG;
384 }
385 else if (vpfnRtlDecryptMemory)
386 {
387 hr = static_cast<HRESULT>(vpfnRtlDecryptMemory(pData, cbData, dwFlags));
388 }
389 else if (vpfnCryptUnprotectMemory)
390 {
391 if (vpfnCryptUnprotectMemory(pData, cbData, dwFlags))
392 {
393 hr = S_OK;
394 }
395 else
396 {
397 hr = HRESULT_FROM_WIN32(::GetLastError());
398 }
399 }
400 CrypExitOnFailure(hr, "Failed to decrypt memory");
401LExit:
402 return hr;
403}
404
diff --git a/src/libs/dutil/WixToolset.DUtil/deputil.cpp b/src/libs/dutil/WixToolset.DUtil/deputil.cpp
new file mode 100644
index 00000000..2e6d6a6c
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/deputil.cpp
@@ -0,0 +1,699 @@
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// Exit macros
7#define DepExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_DEPUTIL, x, s, __VA_ARGS__)
8#define DepExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_DEPUTIL, x, s, __VA_ARGS__)
9#define DepExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_DEPUTIL, x, s, __VA_ARGS__)
10#define DepExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_DEPUTIL, x, s, __VA_ARGS__)
11#define DepExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_DEPUTIL, x, s, __VA_ARGS__)
12#define DepExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_DEPUTIL, x, s, __VA_ARGS__)
13#define DepExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_DEPUTIL, p, x, e, s, __VA_ARGS__)
14#define DepExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_DEPUTIL, p, x, s, __VA_ARGS__)
15#define DepExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_DEPUTIL, p, x, e, s, __VA_ARGS__)
16#define DepExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_DEPUTIL, p, x, s, __VA_ARGS__)
17#define DepExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_DEPUTIL, e, x, s, __VA_ARGS__)
18#define DepExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_DEPUTIL, g, x, s, __VA_ARGS__)
19
20#define ARRAY_GROWTH_SIZE 5
21
22static LPCWSTR vcszVersionValue = L"Version";
23static LPCWSTR vcszDisplayNameValue = L"DisplayName";
24static LPCWSTR vcszMinVersionValue = L"MinVersion";
25static LPCWSTR vcszMaxVersionValue = L"MaxVersion";
26static LPCWSTR vcszAttributesValue = L"Attributes";
27
28enum eRequiresAttributes { RequiresAttributesMinVersionInclusive = 256, RequiresAttributesMaxVersionInclusive = 512 };
29
30// We write to Software\Classes explicitly based on the current security context instead of HKCR.
31// See http://msdn.microsoft.com/en-us/library/ms724475(VS.85).aspx for more information.
32static LPCWSTR vsczRegistryRoot = L"Software\\Classes\\Installer\\Dependencies\\";
33static LPCWSTR vsczRegistryDependents = L"Dependents";
34
35static HRESULT AllocDependencyKeyName(
36 __in_z LPCWSTR wzName,
37 __deref_out_z LPWSTR* psczKeyName
38 );
39
40static HRESULT GetDependencyNameFromKey(
41 __in HKEY hkHive,
42 __in LPCWSTR wzKey,
43 __deref_out_z LPWSTR* psczName
44 );
45
46DAPI_(HRESULT) DepGetProviderInformation(
47 __in HKEY hkHive,
48 __in_z LPCWSTR wzProviderKey,
49 __deref_out_z_opt LPWSTR* psczId,
50 __deref_out_z_opt LPWSTR* psczName,
51 __deref_out_z_opt LPWSTR* psczVersion
52 )
53{
54 HRESULT hr = S_OK;
55 LPWSTR sczKey = NULL;
56 HKEY hkKey = NULL;
57
58 // Format the provider dependency registry key.
59 hr = AllocDependencyKeyName(wzProviderKey, &sczKey);
60 DepExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzProviderKey);
61
62 // Try to open the dependency key.
63 hr = RegOpen(hkHive, sczKey, KEY_READ, &hkKey);
64 if (E_FILENOTFOUND == hr)
65 {
66 ExitFunction1(hr = E_NOTFOUND);
67 }
68 DepExitOnFailure(hr, "Failed to open the registry key for the dependency \"%ls\".", wzProviderKey);
69
70 // Get the Id if requested and available.
71 if (psczId)
72 {
73 hr = RegReadString(hkKey, NULL, psczId);
74 if (E_FILENOTFOUND == hr)
75 {
76 hr = S_OK;
77 }
78 DepExitOnFailure(hr, "Failed to get the id for the dependency \"%ls\".", wzProviderKey);
79 }
80
81 // Get the DisplayName if requested and available.
82 if (psczName)
83 {
84 hr = RegReadString(hkKey, vcszDisplayNameValue, psczName);
85 if (E_FILENOTFOUND == hr)
86 {
87 hr = S_OK;
88 }
89 DepExitOnFailure(hr, "Failed to get the name for the dependency \"%ls\".", wzProviderKey);
90 }
91
92 // Get the Version if requested and available.
93 if (psczVersion)
94 {
95 hr = RegReadString(hkKey, vcszVersionValue, psczVersion);
96 if (E_FILENOTFOUND == hr)
97 {
98 hr = S_OK;
99 }
100 DepExitOnFailure(hr, "Failed to get the version for the dependency \"%ls\".", wzProviderKey);
101 }
102
103LExit:
104 ReleaseRegKey(hkKey);
105 ReleaseStr(sczKey);
106
107 return hr;
108}
109
110DAPI_(HRESULT) DepCheckDependency(
111 __in HKEY hkHive,
112 __in_z LPCWSTR wzProviderKey,
113 __in_z_opt LPCWSTR wzMinVersion,
114 __in_z_opt LPCWSTR wzMaxVersion,
115 __in int iAttributes,
116 __in STRINGDICT_HANDLE sdDependencies,
117 __deref_inout_ecount_opt(*pcDependencies) DEPENDENCY** prgDependencies,
118 __inout LPUINT pcDependencies
119 )
120{
121 HRESULT hr = S_OK;
122 LPWSTR sczKey = NULL;
123 HKEY hkKey = NULL;
124 DWORD64 dw64Version = 0;
125 DWORD64 dw64MinVersion = 0;
126 DWORD64 dw64MaxVersion = 0;
127 BOOL fAllowEqual = FALSE;
128 LPWSTR sczName = NULL;
129
130 // Format the provider dependency registry key.
131 hr = AllocDependencyKeyName(wzProviderKey, &sczKey);
132 DepExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzProviderKey);
133
134 // Try to open the key. If that fails, add the missing dependency key to the dependency array if it doesn't already exist.
135 hr = RegOpen(hkHive, sczKey, KEY_READ, &hkKey);
136 if (E_FILENOTFOUND != hr)
137 {
138 DepExitOnFailure(hr, "Failed to open the registry key for dependency \"%ls\".", wzProviderKey);
139
140 // If there are no registry values, consider the key orphaned and treat it as missing.
141 hr = RegReadVersion(hkKey, vcszVersionValue, &dw64Version);
142 if (E_FILENOTFOUND != hr)
143 {
144 DepExitOnFailure(hr, "Failed to read the %ls registry value for dependency \"%ls\".", vcszVersionValue, wzProviderKey);
145 }
146 }
147
148 // If the key was not found or the Version value was not found, add the missing dependency key to the dependency array.
149 if (E_FILENOTFOUND == hr)
150 {
151 hr = DictKeyExists(sdDependencies, wzProviderKey);
152 if (E_NOTFOUND != hr)
153 {
154 DepExitOnFailure(hr, "Failed to check the dictionary for missing dependency \"%ls\".", wzProviderKey);
155 }
156 else
157 {
158 hr = DepDependencyArrayAlloc(prgDependencies, pcDependencies, wzProviderKey, NULL);
159 DepExitOnFailure(hr, "Failed to add the missing dependency \"%ls\" to the array.", wzProviderKey);
160
161 hr = DictAddKey(sdDependencies, wzProviderKey);
162 DepExitOnFailure(hr, "Failed to add the missing dependency \"%ls\" to the dictionary.", wzProviderKey);
163 }
164
165 // Exit since the check already failed.
166 ExitFunction1(hr = E_NOTFOUND);
167 }
168
169 // Check MinVersion if provided.
170 if (wzMinVersion)
171 {
172 if (*wzMinVersion)
173 {
174 hr = FileVersionFromStringEx(wzMinVersion, 0, &dw64MinVersion);
175 DepExitOnFailure(hr, "Failed to get the 64-bit version number from \"%ls\".", wzMinVersion);
176
177 fAllowEqual = iAttributes & RequiresAttributesMinVersionInclusive;
178 if (!(fAllowEqual && dw64MinVersion <= dw64Version || dw64MinVersion < dw64Version))
179 {
180 hr = DictKeyExists(sdDependencies, wzProviderKey);
181 if (E_NOTFOUND != hr)
182 {
183 DepExitOnFailure(hr, "Failed to check the dictionary for the older dependency \"%ls\".", wzProviderKey);
184 }
185 else
186 {
187 hr = RegReadString(hkKey, vcszDisplayNameValue, &sczName);
188 DepExitOnFailure(hr, "Failed to get the display name of the older dependency \"%ls\".", wzProviderKey);
189
190 hr = DepDependencyArrayAlloc(prgDependencies, pcDependencies, wzProviderKey, sczName);
191 DepExitOnFailure(hr, "Failed to add the older dependency \"%ls\" to the dependencies array.", wzProviderKey);
192
193 hr = DictAddKey(sdDependencies, wzProviderKey);
194 DepExitOnFailure(hr, "Failed to add the older dependency \"%ls\" to the unique dependency string list.", wzProviderKey);
195 }
196
197 // Exit since the check already failed.
198 ExitFunction1(hr = E_NOTFOUND);
199 }
200 }
201 }
202
203 // Check MaxVersion if provided.
204 if (wzMaxVersion)
205 {
206 if (*wzMaxVersion)
207 {
208 hr = FileVersionFromStringEx(wzMaxVersion, 0, &dw64MaxVersion);
209 DepExitOnFailure(hr, "Failed to get the 64-bit version number from \"%ls\".", wzMaxVersion);
210
211 fAllowEqual = iAttributes & RequiresAttributesMaxVersionInclusive;
212 if (!(fAllowEqual && dw64Version <= dw64MaxVersion || dw64Version < dw64MaxVersion))
213 {
214 hr = DictKeyExists(sdDependencies, wzProviderKey);
215 if (E_NOTFOUND != hr)
216 {
217 DepExitOnFailure(hr, "Failed to check the dictionary for the newer dependency \"%ls\".", wzProviderKey);
218 }
219 else
220 {
221 hr = RegReadString(hkKey, vcszDisplayNameValue, &sczName);
222 DepExitOnFailure(hr, "Failed to get the display name of the newer dependency \"%ls\".", wzProviderKey);
223
224 hr = DepDependencyArrayAlloc(prgDependencies, pcDependencies, wzProviderKey, sczName);
225 DepExitOnFailure(hr, "Failed to add the newer dependency \"%ls\" to the dependencies array.", wzProviderKey);
226
227 hr = DictAddKey(sdDependencies, wzProviderKey);
228 DepExitOnFailure(hr, "Failed to add the newer dependency \"%ls\" to the unique dependency string list.", wzProviderKey);
229 }
230
231 // Exit since the check already failed.
232 ExitFunction1(hr = E_NOTFOUND);
233 }
234 }
235 }
236
237LExit:
238 ReleaseStr(sczName);
239 ReleaseRegKey(hkKey);
240 ReleaseStr(sczKey);
241
242 return hr;
243}
244
245DAPI_(HRESULT) DepCheckDependents(
246 __in HKEY hkHive,
247 __in_z LPCWSTR wzProviderKey,
248 __reserved int /*iAttributes*/,
249 __in C_STRINGDICT_HANDLE sdIgnoredDependents,
250 __deref_inout_ecount_opt(*pcDependents) DEPENDENCY** prgDependents,
251 __inout LPUINT pcDependents
252 )
253{
254 HRESULT hr = S_OK;
255 LPWSTR sczKey = NULL;
256 HKEY hkProviderKey = NULL;
257 HKEY hkDependentsKey = NULL;
258 LPWSTR sczDependentKey = NULL;
259 LPWSTR sczDependentName = NULL;
260
261 // Format the provider dependency registry key.
262 hr = AllocDependencyKeyName(wzProviderKey, &sczKey);
263 DepExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzProviderKey);
264
265 // Try to open the key. If that fails, the dependency information is corrupt.
266 hr = RegOpen(hkHive, sczKey, KEY_READ, &hkProviderKey);
267 DepExitOnFailure(hr, "Failed to open the registry key \"%ls\". The dependency store is corrupt.", sczKey);
268
269 // Try to open the dependencies key. If that does not exist, there are no dependents.
270 hr = RegOpen(hkProviderKey, vsczRegistryDependents, KEY_READ, &hkDependentsKey);
271 if (E_FILENOTFOUND != hr)
272 {
273 DepExitOnFailure(hr, "Failed to open the registry key for dependents of \"%ls\".", wzProviderKey);
274 }
275 else
276 {
277 ExitFunction1(hr = S_OK);
278 }
279
280 // Now enumerate the dependent keys. If they are not defined in the ignored list, add them to the array.
281 for (DWORD dwIndex = 0; ; ++dwIndex)
282 {
283 hr = RegKeyEnum(hkDependentsKey, dwIndex, &sczDependentKey);
284 if (E_NOMOREITEMS != hr)
285 {
286 DepExitOnFailure(hr, "Failed to enumerate the dependents key of \"%ls\".", wzProviderKey);
287 }
288 else
289 {
290 hr = S_OK;
291 break;
292 }
293
294 // If the key isn't ignored, add it to the dependent array.
295 hr = DictKeyExists(sdIgnoredDependents, sczDependentKey);
296 if (E_NOTFOUND != hr)
297 {
298 DepExitOnFailure(hr, "Failed to check the dictionary of ignored dependents.");
299 }
300 else
301 {
302 // Get the name of the dependent from the key.
303 hr = GetDependencyNameFromKey(hkHive, sczDependentKey, &sczDependentName);
304 DepExitOnFailure(hr, "Failed to get the name of the dependent from the key \"%ls\".", sczDependentKey);
305
306 hr = DepDependencyArrayAlloc(prgDependents, pcDependents, sczDependentKey, sczDependentName);
307 DepExitOnFailure(hr, "Failed to add the dependent key \"%ls\" to the string array.", sczDependentKey);
308 }
309 }
310
311LExit:
312 ReleaseStr(sczDependentName);
313 ReleaseStr(sczDependentKey);
314 ReleaseRegKey(hkDependentsKey);
315 ReleaseRegKey(hkProviderKey);
316 ReleaseStr(sczKey);
317
318 return hr;
319}
320
321DAPI_(HRESULT) DepRegisterDependency(
322 __in HKEY hkHive,
323 __in_z LPCWSTR wzProviderKey,
324 __in_z LPCWSTR wzVersion,
325 __in_z LPCWSTR wzDisplayName,
326 __in_z_opt LPCWSTR wzId,
327 __in int iAttributes
328 )
329{
330 HRESULT hr = S_OK;
331 LPWSTR sczKey = NULL;
332 HKEY hkKey = NULL;
333 BOOL fCreated = FALSE;
334
335 // Format the provider dependency registry key.
336 hr = AllocDependencyKeyName(wzProviderKey, &sczKey);
337 DepExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzProviderKey);
338
339 // Create the dependency key (or open it if it already exists).
340 hr = RegCreateEx(hkHive, sczKey, KEY_WRITE, FALSE, NULL, &hkKey, &fCreated);
341 DepExitOnFailure(hr, "Failed to create the dependency registry key \"%ls\".", sczKey);
342
343 // Set the id if it was provided.
344 if (wzId)
345 {
346 hr = RegWriteString(hkKey, NULL, wzId);
347 DepExitOnFailure(hr, "Failed to set the %ls registry value to \"%ls\".", L"default", wzId);
348 }
349
350 // Set the version.
351 hr = RegWriteString(hkKey, vcszVersionValue, wzVersion);
352 DepExitOnFailure(hr, "Failed to set the %ls registry value to \"%ls\".", vcszVersionValue, wzVersion);
353
354 // Set the display name.
355 hr = RegWriteString(hkKey, vcszDisplayNameValue, wzDisplayName);
356 DepExitOnFailure(hr, "Failed to set the %ls registry value to \"%ls\".", vcszDisplayNameValue, wzDisplayName);
357
358 // Set the attributes if non-zero.
359 if (0 != iAttributes)
360 {
361 hr = RegWriteNumber(hkKey, vcszAttributesValue, static_cast<DWORD>(iAttributes));
362 DepExitOnFailure(hr, "Failed to set the %ls registry value to %d.", vcszAttributesValue, iAttributes);
363 }
364
365LExit:
366 ReleaseRegKey(hkKey);
367 ReleaseStr(sczKey);
368
369 return hr;
370}
371
372DAPI_(HRESULT) DepDependentExists(
373 __in HKEY hkHive,
374 __in_z LPCWSTR wzDependencyProviderKey,
375 __in_z LPCWSTR wzProviderKey
376 )
377{
378 HRESULT hr = S_OK;
379 LPWSTR sczDependentKey = NULL;
380 HKEY hkDependentKey = NULL;
381
382 // Format the provider dependents registry key.
383 hr = StrAllocFormatted(&sczDependentKey, L"%ls%ls\\%ls\\%ls", vsczRegistryRoot, wzDependencyProviderKey, vsczRegistryDependents, wzProviderKey);
384 DepExitOnFailure(hr, "Failed to format registry key to dependent.");
385
386 hr = RegOpen(hkHive, sczDependentKey, KEY_READ, &hkDependentKey);
387 if (E_FILENOTFOUND != hr)
388 {
389 DepExitOnFailure(hr, "Failed to open the dependent registry key at: \"%ls\".", sczDependentKey);
390 }
391
392LExit:
393 ReleaseRegKey(hkDependentKey);
394 ReleaseStr(sczDependentKey);
395
396 return hr;
397}
398
399DAPI_(HRESULT) DepRegisterDependent(
400 __in HKEY hkHive,
401 __in_z LPCWSTR wzDependencyProviderKey,
402 __in_z LPCWSTR wzProviderKey,
403 __in_z_opt LPCWSTR wzMinVersion,
404 __in_z_opt LPCWSTR wzMaxVersion,
405 __in int iAttributes
406 )
407{
408 HRESULT hr = S_OK;
409 LPWSTR sczDependencyKey = NULL;
410 HKEY hkDependencyKey = NULL;
411 LPWSTR sczKey = NULL;
412 HKEY hkKey = NULL;
413 BOOL fCreated = FALSE;
414
415 // Format the provider dependency registry key.
416 hr = AllocDependencyKeyName(wzDependencyProviderKey, &sczDependencyKey);
417 DepExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzDependencyProviderKey);
418
419 // Create the dependency key (or open it if it already exists).
420 hr = RegCreateEx(hkHive, sczDependencyKey, KEY_WRITE, FALSE, NULL, &hkDependencyKey, &fCreated);
421 DepExitOnFailure(hr, "Failed to create the dependency registry key \"%ls\".", sczDependencyKey);
422
423 // Create the subkey to register the dependent.
424 hr = StrAllocFormatted(&sczKey, L"%ls\\%ls", vsczRegistryDependents, wzProviderKey);
425 DepExitOnFailure(hr, "Failed to allocate dependent subkey \"%ls\" under dependency \"%ls\".", wzProviderKey, wzDependencyProviderKey);
426
427 hr = RegCreateEx(hkDependencyKey, sczKey, KEY_WRITE, FALSE, NULL, &hkKey, &fCreated);
428 DepExitOnFailure(hr, "Failed to create the dependency subkey \"%ls\".", sczKey);
429
430 // Set the minimum version if not NULL.
431 hr = RegWriteString(hkKey, vcszMinVersionValue, wzMinVersion);
432 DepExitOnFailure(hr, "Failed to set the %ls registry value to \"%ls\".", vcszMinVersionValue, wzMinVersion);
433
434 // Set the maximum version if not NULL.
435 hr = RegWriteString(hkKey, vcszMaxVersionValue, wzMaxVersion);
436 DepExitOnFailure(hr, "Failed to set the %ls registry value to \"%ls\".", vcszMaxVersionValue, wzMaxVersion);
437
438 // Set the attributes if non-zero.
439 if (0 != iAttributes)
440 {
441 hr = RegWriteNumber(hkKey, vcszAttributesValue, static_cast<DWORD>(iAttributes));
442 DepExitOnFailure(hr, "Failed to set the %ls registry value to %d.", vcszAttributesValue, iAttributes);
443 }
444
445LExit:
446 ReleaseRegKey(hkKey);
447 ReleaseStr(sczKey);
448 ReleaseRegKey(hkDependencyKey);
449 ReleaseStr(sczDependencyKey);
450
451 return hr;
452}
453
454DAPI_(HRESULT) DepUnregisterDependency(
455 __in HKEY hkHive,
456 __in_z LPCWSTR wzProviderKey
457 )
458{
459 HRESULT hr = S_OK;
460 LPWSTR sczKey = NULL;
461 HKEY hkKey = NULL;
462
463 // Format the provider dependency registry key.
464 hr = AllocDependencyKeyName(wzProviderKey, &sczKey);
465 DepExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzProviderKey);
466
467 // Delete the entire key including all sub-keys.
468 hr = RegDelete(hkHive, sczKey, REG_KEY_DEFAULT, TRUE);
469 if (E_FILENOTFOUND != hr)
470 {
471 DepExitOnFailure(hr, "Failed to delete the key \"%ls\".", sczKey);
472 }
473
474LExit:
475 ReleaseRegKey(hkKey);
476 ReleaseStr(sczKey);
477
478 return hr;
479}
480
481DAPI_(HRESULT) DepUnregisterDependent(
482 __in HKEY hkHive,
483 __in_z LPCWSTR wzDependencyProviderKey,
484 __in_z LPCWSTR wzProviderKey
485 )
486{
487 HRESULT hr = S_OK;
488 HKEY hkRegistryRoot = NULL;
489 HKEY hkDependencyProviderKey = NULL;
490 HKEY hkRegistryDependents = NULL;
491 DWORD cSubKeys = 0;
492 DWORD cValues = 0;
493
494 // Open the root key. We may delete the wzDependencyProviderKey during clean up.
495 hr = RegOpen(hkHive, vsczRegistryRoot, KEY_READ, &hkRegistryRoot);
496 if (E_FILENOTFOUND != hr)
497 {
498 DepExitOnFailure(hr, "Failed to open root registry key \"%ls\".", vsczRegistryRoot);
499 }
500 else
501 {
502 ExitFunction();
503 }
504
505 // Try to open the dependency key. If that does not exist, simply return.
506 hr = RegOpen(hkRegistryRoot, wzDependencyProviderKey, KEY_READ, &hkDependencyProviderKey);
507 if (E_FILENOTFOUND != hr)
508 {
509 DepExitOnFailure(hr, "Failed to open the registry key for the dependency \"%ls\".", wzDependencyProviderKey);
510 }
511 else
512 {
513 ExitFunction();
514 }
515
516 // Try to open the dependents subkey to enumerate.
517 hr = RegOpen(hkDependencyProviderKey, vsczRegistryDependents, KEY_READ, &hkRegistryDependents);
518 if (E_FILENOTFOUND != hr)
519 {
520 DepExitOnFailure(hr, "Failed to open the dependents subkey under the dependency \"%ls\".", wzDependencyProviderKey);
521 }
522 else
523 {
524 ExitFunction();
525 }
526
527 // Delete the wzProviderKey dependent sub-key.
528 hr = RegDelete(hkRegistryDependents, wzProviderKey, REG_KEY_DEFAULT, TRUE);
529 DepExitOnFailure(hr, "Failed to delete the dependent \"%ls\" under the dependency \"%ls\".", wzProviderKey, wzDependencyProviderKey);
530
531 // If there are no remaining dependents, delete the Dependents subkey.
532 hr = RegQueryKey(hkRegistryDependents, &cSubKeys, NULL);
533 DepExitOnFailure(hr, "Failed to get the number of dependent subkeys under the dependency \"%ls\".", wzDependencyProviderKey);
534
535 if (0 < cSubKeys)
536 {
537 ExitFunction();
538 }
539
540 // Release the handle to make sure it's deleted immediately.
541 ReleaseRegKey(hkRegistryDependents);
542
543 // Fail if there are any subkeys since we just checked.
544 hr = RegDelete(hkDependencyProviderKey, vsczRegistryDependents, REG_KEY_DEFAULT, FALSE);
545 DepExitOnFailure(hr, "Failed to delete the dependents subkey under the dependency \"%ls\".", wzDependencyProviderKey);
546
547 // If there are no values, delete the provider dependency key.
548 hr = RegQueryKey(hkDependencyProviderKey, NULL, &cValues);
549 DepExitOnFailure(hr, "Failed to get the number of values under the dependency \"%ls\".", wzDependencyProviderKey);
550
551 if (0 == cValues)
552 {
553 // Release the handle to make sure it's deleted immediately.
554 ReleaseRegKey(hkDependencyProviderKey);
555
556 // Fail if there are any subkeys since we just checked.
557 hr = RegDelete(hkRegistryRoot, wzDependencyProviderKey, REG_KEY_DEFAULT, FALSE);
558 DepExitOnFailure(hr, "Failed to delete the dependency \"%ls\".", wzDependencyProviderKey);
559 }
560
561LExit:
562 ReleaseRegKey(hkRegistryDependents);
563 ReleaseRegKey(hkDependencyProviderKey);
564 ReleaseRegKey(hkRegistryRoot);
565
566 return hr;
567}
568
569DAPI_(HRESULT) DepDependencyArrayAlloc(
570 __deref_inout_ecount_opt(*pcDependencies) DEPENDENCY** prgDependencies,
571 __inout LPUINT pcDependencies,
572 __in_z LPCWSTR wzKey,
573 __in_z_opt LPCWSTR wzName
574 )
575{
576 HRESULT hr = S_OK;
577 UINT cRequired = 0;
578 DEPENDENCY* pDependency = NULL;
579
580 hr = ::UIntAdd(*pcDependencies, 1, &cRequired);
581 DepExitOnFailure(hr, "Failed to increment the number of elements required in the dependency array.");
582
583 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(prgDependencies), cRequired, sizeof(DEPENDENCY), ARRAY_GROWTH_SIZE);
584 DepExitOnFailure(hr, "Failed to allocate memory for the dependency array.");
585
586 pDependency = static_cast<DEPENDENCY*>(&(*prgDependencies)[*pcDependencies]);
587 DepExitOnNull(pDependency, hr, E_POINTER, "The dependency element in the array is invalid.");
588
589 hr = StrAllocString(&(pDependency->sczKey), wzKey, 0);
590 DepExitOnFailure(hr, "Failed to allocate the string key in the dependency array.");
591
592 if (wzName)
593 {
594 hr = StrAllocString(&(pDependency->sczName), wzName, 0);
595 DepExitOnFailure(hr, "Failed to allocate the string name in the dependency array.");
596 }
597
598 // Update the number of current elements in the dependency array.
599 *pcDependencies = cRequired;
600
601LExit:
602 return hr;
603}
604
605DAPI_(void) DepDependencyArrayFree(
606 __in_ecount(cDependencies) DEPENDENCY* rgDependencies,
607 __in UINT cDependencies
608 )
609{
610 for (UINT i = 0; i < cDependencies; ++i)
611 {
612 ReleaseStr(rgDependencies[i].sczKey);
613 ReleaseStr(rgDependencies[i].sczName);
614 }
615
616 ReleaseMem(rgDependencies);
617}
618
619/***************************************************************************
620 AllocDependencyKeyName - Allocates and formats the root registry key name.
621
622***************************************************************************/
623static HRESULT AllocDependencyKeyName(
624 __in_z LPCWSTR wzName,
625 __deref_out_z LPWSTR* psczKeyName
626 )
627{
628 HRESULT hr = S_OK;
629 size_t cchName = 0;
630 size_t cchKeyName = 0;
631
632 // Get the length of the static registry root once.
633 static size_t cchRegistryRoot = ::lstrlenW(vsczRegistryRoot);
634
635 // Get the length of the dependency, and add to the length of the root.
636 hr = ::StringCchLengthW(wzName, STRSAFE_MAX_CCH, &cchName);
637 DepExitOnFailure(hr, "Failed to get string length of dependency name.");
638
639 // Add the sizes together to allocate memory once (callee will add space for nul).
640 hr = ::SizeTAdd(cchRegistryRoot, cchName, &cchKeyName);
641 DepExitOnFailure(hr, "Failed to add the string lengths together.");
642
643 // Allocate and concat the strings together.
644 hr = StrAllocString(psczKeyName, vsczRegistryRoot, cchKeyName);
645 DepExitOnFailure(hr, "Failed to allocate string for dependency registry root.");
646
647 hr = StrAllocConcat(psczKeyName, wzName, cchName);
648 DepExitOnFailure(hr, "Failed to concatenate the dependency key name.");
649
650LExit:
651 return hr;
652}
653
654/***************************************************************************
655 GetDependencyNameFromKey - Attempts to name of the dependency from the key.
656
657***************************************************************************/
658static HRESULT GetDependencyNameFromKey(
659 __in HKEY hkHive,
660 __in LPCWSTR wzProviderKey,
661 __deref_out_z LPWSTR* psczName
662 )
663{
664 HRESULT hr = S_OK;
665 LPWSTR sczKey = NULL;
666 HKEY hkKey = NULL;
667
668 // Format the provider dependency registry key.
669 hr = AllocDependencyKeyName(wzProviderKey, &sczKey);
670 DepExitOnFailure(hr, "Failed to allocate the registry key for dependency \"%ls\".", wzProviderKey);
671
672 // Try to open the dependency key.
673 hr = RegOpen(hkHive, sczKey, KEY_READ, &hkKey);
674 if (E_FILENOTFOUND != hr)
675 {
676 DepExitOnFailure(hr, "Failed to open the registry key for the dependency \"%ls\".", wzProviderKey);
677 }
678 else
679 {
680 ExitFunction1(hr = S_OK);
681 }
682
683 // Get the DisplayName if available.
684 hr = RegReadString(hkKey, vcszDisplayNameValue, psczName);
685 if (E_FILENOTFOUND != hr)
686 {
687 DepExitOnFailure(hr, "Failed to get the dependency name for the dependency \"%ls\".", wzProviderKey);
688 }
689 else
690 {
691 ExitFunction1(hr = S_OK);
692 }
693
694LExit:
695 ReleaseRegKey(hkKey);
696 ReleaseStr(sczKey);
697
698 return hr;
699}
diff --git a/src/libs/dutil/WixToolset.DUtil/dictutil.cpp b/src/libs/dutil/WixToolset.DUtil/dictutil.cpp
new file mode 100644
index 00000000..0d0743eb
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/dictutil.cpp
@@ -0,0 +1,784 @@
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// Exit macros
7#define DictExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_DICTUTIL, x, s, __VA_ARGS__)
8#define DictExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_DICTUTIL, x, s, __VA_ARGS__)
9#define DictExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_DICTUTIL, x, s, __VA_ARGS__)
10#define DictExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_DICTUTIL, x, s, __VA_ARGS__)
11#define DictExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_DICTUTIL, x, s, __VA_ARGS__)
12#define DictExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_DICTUTIL, x, s, __VA_ARGS__)
13#define DictExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_DICTUTIL, p, x, e, s, __VA_ARGS__)
14#define DictExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_DICTUTIL, p, x, s, __VA_ARGS__)
15#define DictExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_DICTUTIL, p, x, e, s, __VA_ARGS__)
16#define DictExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_DICTUTIL, p, x, s, __VA_ARGS__)
17#define DictExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_DICTUTIL, e, x, s, __VA_ARGS__)
18#define DictExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_DICTUTIL, g, x, s, __VA_ARGS__)
19
20// These should all be primes, and spaced reasonably apart (currently each is about 4x the last)
21const DWORD MAX_BUCKET_SIZES[] = {
22 503,
23 2017,
24 7937,
25 32779,
26 131111,
27 524341,
28 2097709,
29 8390857,
30 33563437,
31 134253719,
32 537014927,
33 2148059509
34 };
35
36// However many items are in the cab, let's keep the buckets at least 8 times that to avoid collisions
37#define MAX_BUCKETS_TO_ITEMS_RATIO 8
38
39enum DICT_TYPE
40{
41 DICT_INVALID = 0,
42 DICT_EMBEDDED_KEY = 1,
43 DICT_STRING_LIST = 2
44};
45
46struct STRINGDICT_STRUCT
47{
48 DICT_TYPE dtType;
49
50 // Optional flags to control the behavior of the dictionary.
51 DICT_FLAG dfFlags;
52
53 // Index into MAX_BUCKET_SIZES (array of primes), representing number of buckets we've allocated
54 DWORD dwBucketSizeIndex;
55
56 // Number of items currently stored in the dict buckets
57 DWORD dwNumItems;
58
59 // Byte offset of key within bucket value, for collision checking - see
60 // comments above DictCreateEmbeddedKey() implementation for further details
61 size_t cByteOffset;
62
63 // The actual stored buckets
64 void **ppvBuckets;
65
66 // The actual stored items in the order they were added (used for auto freeing or enumerating)
67 void **ppvItemList;
68
69 // Pointer to the array of items, so the caller is free to resize the array of values out from under us without harm
70 void **ppvValueArray;
71};
72
73const int STRINGDICT_HANDLE_BYTES = sizeof(STRINGDICT_STRUCT);
74
75static HRESULT StringHash(
76 __in const STRINGDICT_STRUCT *psd,
77 __in DWORD dwNumBuckets,
78 __in_z LPCWSTR pszString,
79 __out DWORD *pdwHash
80 );
81static BOOL IsMatchExact(
82 __in const STRINGDICT_STRUCT *psd,
83 __in DWORD dwMatchIndex,
84 __in_z LPCWSTR wzOriginalString
85 );
86static HRESULT GetValue(
87 __in const STRINGDICT_STRUCT *psd,
88 __in_z LPCWSTR pszString,
89 __out_opt void **ppvValue
90 );
91static HRESULT GetInsertIndex(
92 __in const STRINGDICT_STRUCT *psd,
93 __in DWORD dwBucketCount,
94 __in void **ppvBuckets,
95 __in_z LPCWSTR pszString,
96 __out DWORD *pdwOutput
97 );
98static HRESULT GetIndex(
99 __in const STRINGDICT_STRUCT *psd,
100 __in_z LPCWSTR pszString,
101 __out DWORD *pdwOutput
102 );
103static LPCWSTR GetKey(
104 __in const STRINGDICT_STRUCT *psd,
105 __in void *pvValue
106 );
107static HRESULT GrowDictionary(
108 __inout STRINGDICT_STRUCT *psd
109 );
110// These 2 helper functions allow us to safely handle dictutil consumers resizing
111// the value array by storing "offsets" instead of raw void *'s in our buckets.
112static void * TranslateOffsetToValue(
113 __in const STRINGDICT_STRUCT *psd,
114 __in void *pvValue
115 );
116static void * TranslateValueToOffset(
117 __in const STRINGDICT_STRUCT *psd,
118 __in void *pvValue
119 );
120
121// The dict will store a set of keys (as wide-char strings) and a set of values associated with those keys (as void *'s).
122// However, to support collision checking, the key needs to be represented in the "value" object (pointed to
123// by the void *). The "stByteOffset" parameter tells this dict the byte offset of the "key" string pointer
124// within the "value" object. Use the offsetof() macro to fill this out.
125// The "ppvArray" parameter gives dictutil the address of your value array. If you provide this parameter,
126// dictutil will remember all pointer values provided as "offsets" against this array. It is only necessary to provide
127// this parameter to dictutil if it is possible you will realloc the array.
128//
129// Use DictAddValue() and DictGetValue() with this dictionary type.
130extern "C" HRESULT DAPI DictCreateWithEmbeddedKey(
131 __out_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE* psdHandle,
132 __in DWORD dwNumExpectedItems,
133 __in_opt void **ppvArray,
134 __in size_t cByteOffset,
135 __in DICT_FLAG dfFlags
136 )
137{
138 HRESULT hr = S_OK;
139
140 DictExitOnNull(psdHandle, hr, E_INVALIDARG, "Handle not specified while creating dict");
141
142 // Allocate the handle
143 *psdHandle = static_cast<STRINGDICT_HANDLE>(MemAlloc(sizeof(STRINGDICT_STRUCT), FALSE));
144 DictExitOnNull(*psdHandle, hr, E_OUTOFMEMORY, "Failed to allocate dictionary object");
145
146 STRINGDICT_STRUCT *psd = static_cast<STRINGDICT_STRUCT *>(*psdHandle);
147
148 // Fill out the new handle's values
149 psd->dtType = DICT_EMBEDDED_KEY;
150 psd->dfFlags = dfFlags;
151 psd->cByteOffset = cByteOffset;
152 psd->dwBucketSizeIndex = 0;
153 psd->dwNumItems = 0;
154 psd->ppvItemList = NULL;
155 psd->ppvValueArray = ppvArray;
156
157 // Make psd->dwBucketSizeIndex point to the appropriate spot in the prime
158 // array based on expected number of items and items to buckets ratio
159 // Careful: the "-1" in "countof(MAX_BUCKET_SIZES)-1" ensures we don't end
160 // this loop past the end of the array!
161 while (psd->dwBucketSizeIndex < (countof(MAX_BUCKET_SIZES)-1) &&
162 MAX_BUCKET_SIZES[psd->dwBucketSizeIndex] < dwNumExpectedItems * MAX_BUCKETS_TO_ITEMS_RATIO)
163 {
164 ++psd->dwBucketSizeIndex;
165 }
166
167 // Finally, allocate our initial buckets
168 psd->ppvBuckets = static_cast<void**>(MemAlloc(sizeof(void *) * MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], TRUE));
169 DictExitOnNull(psd->ppvBuckets, hr, E_OUTOFMEMORY, "Failed to allocate buckets for dictionary");
170
171LExit:
172 return hr;
173}
174
175// The dict will store a set of keys, with no values associated with them. Use DictAddKey() and DictKeyExists() with this dictionary type.
176extern "C" HRESULT DAPI DictCreateStringList(
177 __out_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE* psdHandle,
178 __in DWORD dwNumExpectedItems,
179 __in DICT_FLAG dfFlags
180 )
181{
182 HRESULT hr = S_OK;
183
184 DictExitOnNull(psdHandle, hr, E_INVALIDARG, "Handle not specified while creating dict");
185
186 // Allocate the handle
187 *psdHandle = static_cast<STRINGDICT_HANDLE>(MemAlloc(sizeof(STRINGDICT_STRUCT), FALSE));
188 DictExitOnNull(*psdHandle, hr, E_OUTOFMEMORY, "Failed to allocate dictionary object");
189
190 STRINGDICT_STRUCT *psd = static_cast<STRINGDICT_STRUCT *>(*psdHandle);
191
192 // Fill out the new handle's values
193 psd->dtType = DICT_STRING_LIST;
194 psd->dfFlags = dfFlags;
195 psd->cByteOffset = 0;
196 psd->dwBucketSizeIndex = 0;
197 psd->dwNumItems = 0;
198 psd->ppvItemList = NULL;
199 psd->ppvValueArray = NULL;
200
201 // Make psd->dwBucketSizeIndex point to the appropriate spot in the prime
202 // array based on expected number of items and items to buckets ratio
203 // Careful: the "-1" in "countof(MAX_BUCKET_SIZES)-1" ensures we don't end
204 // this loop past the end of the array!
205 while (psd->dwBucketSizeIndex < (countof(MAX_BUCKET_SIZES)-1) &&
206 MAX_BUCKET_SIZES[psd->dwBucketSizeIndex] < dwNumExpectedItems * MAX_BUCKETS_TO_ITEMS_RATIO)
207 {
208 ++psd->dwBucketSizeIndex;
209 }
210
211 // Finally, allocate our initial buckets
212 psd->ppvBuckets = static_cast<void**>(MemAlloc(sizeof(void *) * MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], TRUE));
213 DictExitOnNull(psd->ppvBuckets, hr, E_OUTOFMEMORY, "Failed to allocate buckets for dictionary");
214
215LExit:
216 return hr;
217}
218
219extern "C" HRESULT DAPI DictCreateStringListFromArray(
220 __out_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE* psdHandle,
221 __in_ecount(cStringArray) const LPCWSTR* rgwzStringArray,
222 __in const DWORD cStringArray,
223 __in DICT_FLAG dfFlags
224 )
225{
226 HRESULT hr = S_OK;
227 STRINGDICT_HANDLE sd = NULL;
228
229 hr = DictCreateStringList(&sd, cStringArray, dfFlags);
230 DictExitOnFailure(hr, "Failed to create the string dictionary.");
231
232 for (DWORD i = 0; i < cStringArray; ++i)
233 {
234 const LPCWSTR wzKey = rgwzStringArray[i];
235
236 hr = DictKeyExists(sd, wzKey);
237 if (E_NOTFOUND != hr)
238 {
239 DictExitOnFailure(hr, "Failed to check the string dictionary.");
240 }
241 else
242 {
243 hr = DictAddKey(sd, wzKey);
244 DictExitOnFailure(hr, "Failed to add \"%ls\" to the string dictionary.", wzKey);
245 }
246 }
247
248 *psdHandle = sd;
249 sd = NULL;
250
251LExit:
252 ReleaseDict(sd);
253
254 return hr;
255}
256
257extern "C" HRESULT DAPI DictCompareStringListToArray(
258 __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdStringList,
259 __in_ecount(cStringArray) const LPCWSTR* rgwzStringArray,
260 __in const DWORD cStringArray
261 )
262{
263 HRESULT hr = S_OK;
264
265 for (DWORD i = 0; i < cStringArray; ++i)
266 {
267 hr = DictKeyExists(sdStringList, rgwzStringArray[i]);
268 if (E_NOTFOUND != hr)
269 {
270 DictExitOnFailure(hr, "Failed to check the string dictionary.");
271 ExitFunction1(hr = S_OK);
272 }
273 }
274
275 ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_NO_MATCH));
276
277LExit:
278 return hr;
279}
280
281// Todo: Dict should resize itself when (number of items) exceeds (number of buckets / MAX_BUCKETS_TO_ITEMS_RATIO)
282extern "C" HRESULT DAPI DictAddKey(
283 __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdHandle,
284 __in_z LPCWSTR pszString
285 )
286{
287 HRESULT hr = S_OK;
288 DWORD dwIndex = 0;
289 STRINGDICT_STRUCT *psd = static_cast<STRINGDICT_STRUCT *>(sdHandle);
290
291 DictExitOnNull(sdHandle, hr, E_INVALIDARG, "Handle not specified while adding value to dict");
292 DictExitOnNull(pszString, hr, E_INVALIDARG, "String not specified while adding value to dict");
293
294 if (psd->dwBucketSizeIndex >= countof(MAX_BUCKET_SIZES))
295 {
296 hr = E_INVALIDARG;
297 DictExitOnFailure(hr, "Invalid dictionary - bucket size index is out of range");
298 }
299
300 if (DICT_STRING_LIST != psd->dtType)
301 {
302 hr = E_INVALIDARG;
303 DictExitOnFailure(hr, "Tried to add key without value to wrong dictionary type! This dictionary type is: %d", psd->dtType);
304 }
305
306 if ((psd->dwNumItems + 1) >= MAX_BUCKET_SIZES[psd->dwBucketSizeIndex] / MAX_BUCKETS_TO_ITEMS_RATIO)
307 {
308 hr = GrowDictionary(psd);
309 if (HRESULT_FROM_WIN32(ERROR_DATABASE_FULL) == hr)
310 {
311 // If we fail to proactively grow the dictionary, don't fail unless the dictionary is completely full
312 if (psd->dwNumItems < MAX_BUCKET_SIZES[psd->dwBucketSizeIndex])
313 {
314 hr = S_OK;
315 }
316 }
317 DictExitOnFailure(hr, "Failed to grow dictionary");
318 }
319
320 hr = GetInsertIndex(psd, MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], psd->ppvBuckets, pszString, &dwIndex);
321 DictExitOnFailure(hr, "Failed to get index to insert into");
322
323 hr = MemEnsureArraySize(reinterpret_cast<void **>(&(psd->ppvItemList)), psd->dwNumItems + 1, sizeof(void *), 1000);
324 DictExitOnFailure(hr, "Failed to resize list of items in dictionary");
325 ++psd->dwNumItems;
326
327 hr = StrAllocString(reinterpret_cast<LPWSTR *>(&(psd->ppvBuckets[dwIndex])), pszString, 0);
328 DictExitOnFailure(hr, "Failed to allocate copy of string");
329
330 psd->ppvItemList[psd->dwNumItems-1] = psd->ppvBuckets[dwIndex];
331
332LExit:
333 return hr;
334}
335
336// Todo: Dict should resize itself when (number of items) exceeds (number of buckets / MAX_BUCKETS_TO_ITEMS_RATIO)
337extern "C" HRESULT DAPI DictAddValue(
338 __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdHandle,
339 __in void *pvValue
340 )
341{
342 HRESULT hr = S_OK;
343 void *pvOffset = NULL;
344 LPCWSTR wzKey = NULL;
345 DWORD dwIndex = 0;
346 STRINGDICT_STRUCT *psd = static_cast<STRINGDICT_STRUCT *>(sdHandle);
347
348 DictExitOnNull(sdHandle, hr, E_INVALIDARG, "Handle not specified while adding value to dict");
349 DictExitOnNull(pvValue, hr, E_INVALIDARG, "Value not specified while adding value to dict");
350
351 if (psd->dwBucketSizeIndex >= countof(MAX_BUCKET_SIZES))
352 {
353 hr = E_INVALIDARG;
354 DictExitOnFailure(hr, "Invalid dictionary - bucket size index is out of range");
355 }
356
357 if (DICT_EMBEDDED_KEY != psd->dtType)
358 {
359 hr = E_INVALIDARG;
360 DictExitOnFailure(hr, "Tried to add key/value pair to wrong dictionary type! This dictionary type is: %d", psd->dtType);
361 }
362
363 wzKey = GetKey(psd, pvValue);
364 DictExitOnNull(wzKey, hr, E_INVALIDARG, "String not specified while adding value to dict");
365
366 if ((psd->dwNumItems + 1) >= MAX_BUCKET_SIZES[psd->dwBucketSizeIndex] / MAX_BUCKETS_TO_ITEMS_RATIO)
367 {
368 hr = GrowDictionary(psd);
369 if (HRESULT_FROM_WIN32(ERROR_DATABASE_FULL) == hr && psd->dwNumItems + 1 )
370 {
371 // If we fail to proactively grow the dictionary, don't fail unless the dictionary is completely full
372 if (psd->dwNumItems < MAX_BUCKET_SIZES[psd->dwBucketSizeIndex])
373 {
374 hr = S_OK;
375 }
376 }
377 DictExitOnFailure(hr, "Failed to grow dictionary");
378 }
379
380 hr = GetInsertIndex(psd, MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], psd->ppvBuckets, wzKey, &dwIndex);
381 DictExitOnFailure(hr, "Failed to get index to insert into");
382
383 hr = MemEnsureArraySize(reinterpret_cast<void **>(&(psd->ppvItemList)), psd->dwNumItems + 1, sizeof(void *), 1000);
384 DictExitOnFailure(hr, "Failed to resize list of items in dictionary");
385 ++psd->dwNumItems;
386
387 pvOffset = TranslateValueToOffset(psd, pvValue);
388 psd->ppvBuckets[dwIndex] = pvOffset;
389 psd->ppvItemList[psd->dwNumItems-1] = pvOffset;
390
391LExit:
392 return hr;
393}
394
395extern "C" HRESULT DAPI DictGetValue(
396 __in_bcount(STRINGDICT_HANDLE_BYTES) C_STRINGDICT_HANDLE sdHandle,
397 __in_z LPCWSTR pszString,
398 __out void **ppvValue
399 )
400{
401 HRESULT hr = S_OK;
402
403 DictExitOnNull(sdHandle, hr, E_INVALIDARG, "Handle not specified while searching dict");
404 DictExitOnNull(pszString, hr, E_INVALIDARG, "String not specified while searching dict");
405
406 const STRINGDICT_STRUCT *psd = static_cast<const STRINGDICT_STRUCT *>(sdHandle);
407
408 if (DICT_EMBEDDED_KEY != psd->dtType)
409 {
410 hr = E_INVALIDARG;
411 DictExitOnFailure(hr, "Tried to lookup value in wrong dictionary type! This dictionary type is: %d", psd->dtType);
412 }
413
414 hr = GetValue(psd, pszString, ppvValue);
415 if (E_NOTFOUND == hr)
416 {
417 ExitFunction();
418 }
419 DictExitOnFailure(hr, "Failed to call internal GetValue()");
420
421LExit:
422 return hr;
423}
424
425extern "C" HRESULT DAPI DictKeyExists(
426 __in_bcount(STRINGDICT_HANDLE_BYTES) C_STRINGDICT_HANDLE sdHandle,
427 __in_z LPCWSTR pszString
428 )
429{
430 HRESULT hr = S_OK;
431
432 DictExitOnNull(sdHandle, hr, E_INVALIDARG, "Handle not specified while searching dict");
433 DictExitOnNull(pszString, hr, E_INVALIDARG, "String not specified while searching dict");
434
435 const STRINGDICT_STRUCT *psd = static_cast<const STRINGDICT_STRUCT *>(sdHandle);
436
437 // This works with either type of dictionary
438 hr = GetValue(psd, pszString, NULL);
439 if (E_NOTFOUND == hr)
440 {
441 ExitFunction();
442 }
443 DictExitOnFailure(hr, "Failed to call internal GetValue()");
444
445LExit:
446 return hr;
447}
448
449extern "C" void DAPI DictDestroy(
450 __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdHandle
451 )
452{
453 DWORD i;
454
455 STRINGDICT_STRUCT *psd = static_cast<STRINGDICT_STRUCT *>(sdHandle);
456
457 if (DICT_STRING_LIST == psd->dtType)
458 {
459 for (i = 0; i < psd->dwNumItems; ++i)
460 {
461 ReleaseStr(reinterpret_cast<LPWSTR>(psd->ppvItemList[i]));
462 }
463 }
464
465 ReleaseMem(psd->ppvItemList);
466 ReleaseMem(psd->ppvBuckets);
467 ReleaseMem(psd);
468}
469
470static HRESULT StringHash(
471 __in const STRINGDICT_STRUCT *psd,
472 __in DWORD dwNumBuckets,
473 __in_z LPCWSTR pszString,
474 __out DWORD *pdwHash
475 )
476{
477 HRESULT hr = S_OK;
478 LPCWSTR wzKey = NULL;
479 LPWSTR sczNewKey = NULL;
480 DWORD result = 0;
481
482 if (DICT_FLAG_CASEINSENSITIVE & psd->dfFlags)
483 {
484 hr = StrAllocStringToUpperInvariant(&sczNewKey, pszString, 0);
485 DictExitOnFailure(hr, "Failed to convert the string to upper-case.");
486
487 wzKey = sczNewKey;
488 }
489 else
490 {
491 wzKey = pszString;
492 }
493
494 while (*wzKey)
495 {
496 result = ~(*wzKey++ * 509) + result * 65599;
497 }
498
499 *pdwHash = result % dwNumBuckets;
500
501LExit:
502 ReleaseStr(sczNewKey);
503
504 return hr;
505}
506
507static BOOL IsMatchExact(
508 __in const STRINGDICT_STRUCT *psd,
509 __in DWORD dwMatchIndex,
510 __in_z LPCWSTR wzOriginalString
511 )
512{
513 LPCWSTR wzMatchString = GetKey(psd, TranslateOffsetToValue(psd, psd->ppvBuckets[dwMatchIndex]));
514 DWORD dwFlags = 0;
515
516 if (DICT_FLAG_CASEINSENSITIVE & psd->dfFlags)
517 {
518 dwFlags |= NORM_IGNORECASE;
519 }
520
521 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, dwFlags, wzOriginalString, -1, wzMatchString, -1))
522 {
523 return TRUE;
524 }
525
526 return FALSE;
527}
528
529static HRESULT GetValue(
530 __in const STRINGDICT_STRUCT *psd,
531 __in_z LPCWSTR pszString,
532 __out_opt void **ppvValue
533 )
534{
535 HRESULT hr = S_OK;
536 DWORD dwOriginalIndexCandidate = 0;
537 void *pvCandidateValue = NULL;
538 DWORD dwIndex = 0;
539
540 DictExitOnNull(psd, hr, E_INVALIDARG, "Handle not specified while searching dict");
541 DictExitOnNull(pszString, hr, E_INVALIDARG, "String not specified while searching dict");
542
543 if (psd->dwBucketSizeIndex >= countof(MAX_BUCKET_SIZES))
544 {
545 hr = E_INVALIDARG;
546 DictExitOnFailure(hr, "Invalid dictionary - bucket size index is out of range");
547 }
548
549 hr = StringHash(psd, MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], pszString, &dwOriginalIndexCandidate);
550 DictExitOnFailure(hr, "Failed to hash the string.");
551
552 DWORD dwIndexCandidate = dwOriginalIndexCandidate;
553
554 pvCandidateValue = TranslateOffsetToValue(psd, psd->ppvBuckets[dwIndexCandidate]);
555
556 // If no match exists in the dict
557 if (NULL == pvCandidateValue)
558 {
559 if (NULL != ppvValue)
560 {
561 *ppvValue = NULL;
562 }
563 ExitFunction1(hr = E_NOTFOUND);
564 }
565
566 hr = GetIndex(psd, pszString, &dwIndex);
567 if (E_NOTFOUND == hr)
568 {
569 ExitFunction();
570 }
571 DictExitOnFailure(hr, "Failed to find index to get");
572
573 if (NULL != ppvValue)
574 {
575 *ppvValue = TranslateOffsetToValue(psd, psd->ppvBuckets[dwIndex]);
576 }
577
578LExit:
579 if (FAILED(hr) && NULL != ppvValue)
580 {
581 *ppvValue = NULL;
582 }
583
584 return hr;
585}
586
587static HRESULT GetInsertIndex(
588 __in const STRINGDICT_STRUCT *psd,
589 __in DWORD dwBucketCount,
590 __in void **ppvBuckets,
591 __in_z LPCWSTR pszString,
592 __out DWORD *pdwOutput
593 )
594{
595 HRESULT hr = S_OK;
596 DWORD dwOriginalIndexCandidate = 0;
597
598 hr = StringHash(psd, dwBucketCount, pszString, &dwOriginalIndexCandidate);
599 DictExitOnFailure(hr, "Failed to hash the string.");
600
601 DWORD dwIndexCandidate = dwOriginalIndexCandidate;
602
603 // If we collide, keep iterating forward from our intended position, even wrapping around to zero, until we find an empty bucket
604#pragma prefast(push)
605#pragma prefast(disable:26007)
606 while (NULL != ppvBuckets[dwIndexCandidate])
607#pragma prefast(pop)
608 {
609 ++dwIndexCandidate;
610
611 // If we got to the end of the array, wrap around to zero index
612 if (dwIndexCandidate >= dwBucketCount)
613 {
614 dwIndexCandidate = 0;
615 }
616
617 // If we wrapped all the way back around to our original index, the dict is full - throw an error
618 if (dwIndexCandidate == dwOriginalIndexCandidate)
619 {
620 // The dict table is full - this error seems to be a reasonably close match
621 hr = HRESULT_FROM_WIN32(ERROR_DATABASE_FULL);
622 DictExitOnRootFailure(hr, "Failed to add item '%ls' to dict table because dict table is full of items", pszString);
623 }
624 }
625
626 *pdwOutput = dwIndexCandidate;
627
628LExit:
629 return hr;
630}
631
632static HRESULT GetIndex(
633 __in const STRINGDICT_STRUCT *psd,
634 __in_z LPCWSTR pszString,
635 __out DWORD *pdwOutput
636 )
637{
638 HRESULT hr = S_OK;
639 DWORD dwOriginalIndexCandidate = 0;
640
641 if (psd->dwBucketSizeIndex >= countof(MAX_BUCKET_SIZES))
642 {
643 hr = E_INVALIDARG;
644 DictExitOnFailure(hr, "Invalid dictionary - bucket size index is out of range");
645 }
646
647 hr = StringHash(psd, MAX_BUCKET_SIZES[psd->dwBucketSizeIndex], pszString, &dwOriginalIndexCandidate);
648 DictExitOnFailure(hr, "Failed to hash the string.");
649
650 DWORD dwIndexCandidate = dwOriginalIndexCandidate;
651
652 while (!IsMatchExact(psd, dwIndexCandidate, pszString))
653 {
654 ++dwIndexCandidate;
655
656 // If we got to the end of the array, wrap around to zero index
657 if (dwIndexCandidate >= MAX_BUCKET_SIZES[psd->dwBucketSizeIndex])
658 {
659 dwIndexCandidate = 0;
660 }
661
662 // If no match exists in the dict
663 if (NULL == psd->ppvBuckets[dwIndexCandidate])
664 {
665 ExitFunction1(hr = E_NOTFOUND);
666 }
667
668 // If we wrapped all the way back around to our original index, the dict is full and we found nothing, so return as such
669 if (dwIndexCandidate == dwOriginalIndexCandidate)
670 {
671 ExitFunction1(hr = E_NOTFOUND);
672 }
673 }
674
675 *pdwOutput = dwIndexCandidate;
676
677LExit:
678 return hr;
679}
680
681static LPCWSTR GetKey(
682 __in const STRINGDICT_STRUCT *psd,
683 __in void *pvValue
684 )
685{
686 const BYTE *lpByte = reinterpret_cast<BYTE *>(pvValue);
687
688 if (DICT_EMBEDDED_KEY == psd->dtType)
689 {
690 void *pvKey = reinterpret_cast<void *>(reinterpret_cast<BYTE *>(pvValue) + psd->cByteOffset);
691
692#pragma prefast(push)
693#pragma prefast(disable:26010)
694 return *(reinterpret_cast<LPCWSTR *>(pvKey));
695#pragma prefast(pop)
696 }
697 else
698 {
699 return (reinterpret_cast<LPCWSTR>(lpByte));
700 }
701}
702
703static HRESULT GrowDictionary(
704 __inout STRINGDICT_STRUCT *psd
705 )
706{
707 HRESULT hr = S_OK;
708 DWORD dwInsertIndex = 0;
709 LPCWSTR wzKey = NULL;
710 DWORD dwNewBucketSizeIndex = 0;
711 size_t cbAllocSize = 0;
712 void **ppvNewBuckets = NULL;
713
714 dwNewBucketSizeIndex = psd->dwBucketSizeIndex + 1;
715
716 if (dwNewBucketSizeIndex >= countof(MAX_BUCKET_SIZES))
717 {
718 ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_DATABASE_FULL));
719 }
720
721 hr = ::SizeTMult(sizeof(void *), MAX_BUCKET_SIZES[dwNewBucketSizeIndex], &cbAllocSize);
722 DictExitOnFailure(hr, "Overflow while calculating allocation size to grow dictionary");
723
724 ppvNewBuckets = static_cast<void**>(MemAlloc(cbAllocSize, TRUE));
725 DictExitOnNull(ppvNewBuckets, hr, E_OUTOFMEMORY, "Failed to allocate %u buckets while growing dictionary", MAX_BUCKET_SIZES[dwNewBucketSizeIndex]);
726
727 for (DWORD i = 0; i < psd->dwNumItems; ++i)
728 {
729 wzKey = GetKey(psd, TranslateOffsetToValue(psd, psd->ppvItemList[i]));
730 DictExitOnNull(wzKey, hr, E_INVALIDARG, "String not specified in existing dict value");
731
732 hr = GetInsertIndex(psd, MAX_BUCKET_SIZES[dwNewBucketSizeIndex], ppvNewBuckets, wzKey, &dwInsertIndex);
733 DictExitOnFailure(hr, "Failed to get index to insert into");
734
735 ppvNewBuckets[dwInsertIndex] = psd->ppvItemList[i];
736 }
737
738 psd->dwBucketSizeIndex = dwNewBucketSizeIndex;
739 ReleaseMem(psd->ppvBuckets);
740 psd->ppvBuckets = ppvNewBuckets;
741 ppvNewBuckets = NULL;
742
743LExit:
744 ReleaseMem(ppvNewBuckets);
745
746 return hr;
747}
748
749static void * TranslateOffsetToValue(
750 __in const STRINGDICT_STRUCT *psd,
751 __in void *pvValue
752 )
753{
754 if (NULL == pvValue)
755 {
756 return NULL;
757 }
758
759 // All offsets are stored as (real offset + 1), so subtract 1 to get back to the real value
760 if (NULL != psd->ppvValueArray)
761 {
762 return reinterpret_cast<void *>(reinterpret_cast<DWORD_PTR>(pvValue) + reinterpret_cast<DWORD_PTR>(*psd->ppvValueArray) - 1);
763 }
764 else
765 {
766 return pvValue;
767 }
768}
769
770static void * TranslateValueToOffset(
771 __in const STRINGDICT_STRUCT *psd,
772 __in void *pvValue
773 )
774{
775 if (NULL != psd->ppvValueArray)
776 {
777 // 0 has a special meaning - we don't want offset 0 into the array to have NULL for the offset - so add 1 to avoid this issue
778 return reinterpret_cast<void *>(reinterpret_cast<DWORD_PTR>(pvValue) - reinterpret_cast<DWORD_PTR>(*psd->ppvValueArray) + 1);
779 }
780 else
781 {
782 return pvValue;
783 }
784}
diff --git a/src/libs/dutil/WixToolset.DUtil/dirutil.cpp b/src/libs/dutil/WixToolset.DUtil/dirutil.cpp
new file mode 100644
index 00000000..ae2c5e1c
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/dirutil.cpp
@@ -0,0 +1,441 @@
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// Exit macros
7#define DirExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_DIRUTIL, x, s, __VA_ARGS__)
8#define DirExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_DIRUTIL, x, s, __VA_ARGS__)
9#define DirExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_DIRUTIL, x, s, __VA_ARGS__)
10#define DirExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_DIRUTIL, x, s, __VA_ARGS__)
11#define DirExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_DIRUTIL, x, s, __VA_ARGS__)
12#define DirExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_DIRUTIL, x, s, __VA_ARGS__)
13#define DirExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_DIRUTIL, p, x, e, s, __VA_ARGS__)
14#define DirExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_DIRUTIL, p, x, s, __VA_ARGS__)
15#define DirExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_DIRUTIL, p, x, e, s, __VA_ARGS__)
16#define DirExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_DIRUTIL, p, x, s, __VA_ARGS__)
17#define DirExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_DIRUTIL, e, x, s, __VA_ARGS__)
18#define DirExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_DIRUTIL, g, x, s, __VA_ARGS__)
19
20
21/*******************************************************************
22 DirExists
23
24*******************************************************************/
25extern "C" BOOL DAPI DirExists(
26 __in_z LPCWSTR wzPath,
27 __out_opt DWORD *pdwAttributes
28 )
29{
30 Assert(wzPath);
31
32 BOOL fExists = FALSE;
33
34 DWORD dwAttributes = ::GetFileAttributesW(wzPath);
35 if (0xFFFFFFFF == dwAttributes) // TODO: figure out why "INVALID_FILE_ATTRIBUTES" can't be used here
36 {
37 ExitFunction();
38 }
39
40 if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY)
41 {
42 if (pdwAttributes)
43 {
44 *pdwAttributes = dwAttributes;
45 }
46
47 fExists = TRUE;
48 }
49
50LExit:
51 return fExists;
52}
53
54
55/*******************************************************************
56 DirCreateTempPath
57
58 *******************************************************************/
59extern "C" HRESULT DAPI DirCreateTempPath(
60 __in_z LPCWSTR wzPrefix,
61 __out_ecount_z(cchPath) LPWSTR wzPath,
62 __in DWORD cchPath
63 )
64{
65 Assert(wzPrefix);
66 Assert(wzPath);
67
68 HRESULT hr = S_OK;
69
70 WCHAR wzDir[MAX_PATH];
71 WCHAR wzFile[MAX_PATH];
72 DWORD cch = 0;
73
74 cch = ::GetTempPathW(countof(wzDir), wzDir);
75 if (!cch || cch >= countof(wzDir))
76 {
77 DirExitWithLastError(hr, "Failed to GetTempPath.");
78 }
79
80 if (!::GetTempFileNameW(wzDir, wzPrefix, 0, wzFile))
81 {
82 DirExitWithLastError(hr, "Failed to GetTempFileName.");
83 }
84
85 hr = ::StringCchCopyW(wzPath, cchPath, wzFile);
86
87LExit:
88 return hr;
89}
90
91
92/*******************************************************************
93 DirEnsureExists
94
95*******************************************************************/
96extern "C" HRESULT DAPI DirEnsureExists(
97 __in_z LPCWSTR wzPath,
98 __in_opt LPSECURITY_ATTRIBUTES psa
99 )
100{
101 HRESULT hr = S_OK;
102 UINT er;
103
104 // try to create this directory
105 if (!::CreateDirectoryW(wzPath, psa))
106 {
107 // if the directory already exists, bail
108 er = ::GetLastError();
109 if (ERROR_ALREADY_EXISTS == er)
110 {
111 ExitFunction1(hr = S_OK);
112 }
113 else if (ERROR_PATH_NOT_FOUND != er && DirExists(wzPath, NULL)) // if the directory happens to exist (don't check if CreateDirectory said it doesn't), declare success.
114 {
115 ExitFunction1(hr = S_OK);
116 }
117
118 // get the parent path and try to create it
119 LPWSTR pwzLastSlash = NULL;
120 for (LPWSTR pwz = const_cast<LPWSTR>(wzPath); *pwz; ++pwz)
121 {
122 if (*pwz == L'\\')
123 {
124 pwzLastSlash = pwz;
125 }
126 }
127
128 // if there is no parent directory fail
129 DirExitOnNullDebugTrace(pwzLastSlash, hr, HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND), "cannot find parent path");
130
131 *pwzLastSlash = L'\0'; // null terminate the parent path
132 hr = DirEnsureExists(wzPath, psa); // recurse!
133 *pwzLastSlash = L'\\'; // put the slash back
134 DirExitOnFailureDebugTrace(hr, "failed to create path: %ls", wzPath);
135
136 // try to create the directory now that all parents are created
137 if (!::CreateDirectoryW(wzPath, psa))
138 {
139 // if the directory already exists for some reason no error
140 er = ::GetLastError();
141 if (ERROR_ALREADY_EXISTS == er)
142 {
143 hr = S_FALSE;
144 }
145 else
146 {
147 hr = HRESULT_FROM_WIN32(er);
148 }
149 }
150 else
151 {
152 hr = S_OK;
153 }
154 }
155
156LExit:
157 return hr;
158}
159
160
161/*******************************************************************
162 DirEnsureDelete - removes an entire directory structure
163
164*******************************************************************/
165extern "C" HRESULT DAPI DirEnsureDelete(
166 __in_z LPCWSTR wzPath,
167 __in BOOL fDeleteFiles,
168 __in BOOL fRecurse
169 )
170{
171 HRESULT hr = S_OK;
172 DWORD dwDeleteFlags = 0;
173
174 dwDeleteFlags |= fDeleteFiles ? DIR_DELETE_FILES : 0;
175 dwDeleteFlags |= fRecurse ? DIR_DELETE_RECURSE : 0;
176
177 hr = DirEnsureDeleteEx(wzPath, dwDeleteFlags);
178 return hr;
179}
180
181
182/*******************************************************************
183 DirEnsureDeleteEx - removes an entire directory structure
184
185*******************************************************************/
186extern "C" HRESULT DAPI DirEnsureDeleteEx(
187 __in_z LPCWSTR wzPath,
188 __in DWORD dwFlags
189 )
190{
191 Assert(wzPath && *wzPath);
192
193 HRESULT hr = S_OK;
194 DWORD er;
195
196 DWORD dwAttrib;
197 HANDLE hFind = INVALID_HANDLE_VALUE;
198 LPWSTR sczDelete = NULL;
199 WIN32_FIND_DATAW wfd;
200
201 BOOL fDeleteFiles = (DIR_DELETE_FILES == (dwFlags & DIR_DELETE_FILES));
202 BOOL fRecurse = (DIR_DELETE_RECURSE == (dwFlags & DIR_DELETE_RECURSE));
203 BOOL fScheduleDelete = (DIR_DELETE_SCHEDULE == (dwFlags & DIR_DELETE_SCHEDULE));
204 WCHAR wzTempDirectory[MAX_PATH] = { };
205 WCHAR wzTempPath[MAX_PATH] = { };
206
207 if (-1 == (dwAttrib = ::GetFileAttributesW(wzPath)))
208 {
209 er = ::GetLastError();
210 if (ERROR_FILE_NOT_FOUND == er) // change "file not found" to "path not found" since we were looking for a directory.
211 {
212 er = ERROR_PATH_NOT_FOUND;
213 }
214 hr = HRESULT_FROM_WIN32(er);
215 DirExitOnRootFailure(hr, "Failed to get attributes for path: %ls", wzPath);
216 }
217
218 if (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)
219 {
220 if (dwAttrib & FILE_ATTRIBUTE_READONLY)
221 {
222 if (!::SetFileAttributesW(wzPath, FILE_ATTRIBUTE_NORMAL))
223 {
224 DirExitWithLastError(hr, "Failed to remove read-only attribute from path: %ls", wzPath);
225 }
226 }
227
228 // If we're deleting files and/or child directories loop through the contents of the directory.
229 if (fDeleteFiles || fRecurse)
230 {
231 if (fScheduleDelete)
232 {
233 if (!::GetTempPathW(countof(wzTempDirectory), wzTempDirectory))
234 {
235 DirExitWithLastError(hr, "Failed to get temp directory.");
236 }
237 }
238
239 // Delete everything in this directory.
240 hr = PathConcat(wzPath, L"*.*", &sczDelete);
241 DirExitOnFailure(hr, "Failed to concat wild cards to string: %ls", wzPath);
242
243 hFind = ::FindFirstFileW(sczDelete, &wfd);
244 if (INVALID_HANDLE_VALUE == hFind)
245 {
246 DirExitWithLastError(hr, "failed to get first file in directory: %ls", wzPath);
247 }
248
249 do
250 {
251 // Skip the dot directories.
252 if (L'.' == wfd.cFileName[0] && (L'\0' == wfd.cFileName[1] || (L'.' == wfd.cFileName[1] && L'\0' == wfd.cFileName[2])))
253 {
254 continue;
255 }
256
257 // For extra safety and to silence OACR.
258 wfd.cFileName[MAX_PATH - 1] = L'\0';
259
260 hr = PathConcat(wzPath, wfd.cFileName, &sczDelete);
261 DirExitOnFailure(hr, "Failed to concat filename '%ls' to directory: %ls", wfd.cFileName, wzPath);
262
263 if (fRecurse && wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
264 {
265 hr = PathBackslashTerminate(&sczDelete);
266 DirExitOnFailure(hr, "Failed to ensure path is backslash terminated: %ls", sczDelete);
267
268 hr = DirEnsureDeleteEx(sczDelete, dwFlags); // recursive call
269 if (FAILED(hr))
270 {
271 // if we failed to delete a subdirectory, keep trying to finish any remaining files
272 ExitTraceSource(DUTIL_SOURCE_DIRUTIL, hr, "Failed to delete subdirectory; continuing: %ls", sczDelete);
273 hr = S_OK;
274 }
275 }
276 else if (fDeleteFiles) // this is a file, just delete it
277 {
278 if (wfd.dwFileAttributes & FILE_ATTRIBUTE_READONLY || wfd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN || wfd.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM)
279 {
280 if (!::SetFileAttributesW(sczDelete, FILE_ATTRIBUTE_NORMAL))
281 {
282 DirExitWithLastError(hr, "Failed to remove attributes from file: %ls", sczDelete);
283 }
284 }
285
286 if (!::DeleteFileW(sczDelete))
287 {
288 if (fScheduleDelete)
289 {
290 if (!::GetTempFileNameW(wzTempDirectory, L"DEL", 0, wzTempPath))
291 {
292 DirExitWithLastError(hr, "Failed to get temp file to move to.");
293 }
294
295 // Try to move the file to the temp directory then schedule for delete,
296 // otherwise just schedule for delete.
297 if (::MoveFileExW(sczDelete, wzTempPath, MOVEFILE_REPLACE_EXISTING))
298 {
299 ::MoveFileExW(wzTempPath, NULL, MOVEFILE_DELAY_UNTIL_REBOOT);
300 }
301 else
302 {
303 ::MoveFileExW(sczDelete, NULL, MOVEFILE_DELAY_UNTIL_REBOOT);
304 }
305 }
306 else
307 {
308 DirExitWithLastError(hr, "Failed to delete file: %ls", sczDelete);
309 }
310 }
311 }
312 } while (::FindNextFileW(hFind, &wfd));
313
314 er = ::GetLastError();
315 if (ERROR_NO_MORE_FILES == er)
316 {
317 hr = S_OK;
318 }
319 else
320 {
321 DirExitWithLastError(hr, "Failed while looping through files in directory: %ls", wzPath);
322 }
323 }
324
325 if (!::RemoveDirectoryW(wzPath))
326 {
327 hr = HRESULT_FROM_WIN32(::GetLastError());
328 if (HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION) == hr && fScheduleDelete)
329 {
330 if (::MoveFileExW(wzPath, NULL, MOVEFILE_DELAY_UNTIL_REBOOT))
331 {
332 hr = S_OK;
333 }
334 }
335
336 DirExitOnRootFailure(hr, "Failed to remove directory: %ls", wzPath);
337 }
338 }
339 else
340 {
341 hr = E_UNEXPECTED;
342 DirExitOnFailure(hr, "Directory delete cannot delete file: %ls", wzPath);
343 }
344
345 Assert(S_OK == hr);
346
347LExit:
348 ReleaseFileFindHandle(hFind);
349 ReleaseStr(sczDelete);
350
351 return hr;
352}
353
354
355/*******************************************************************
356DirDeleteEmptyDirectoriesToRoot - removes an empty directory and as many
357 of its parents as possible.
358
359 Returns: count of directories deleted.
360*******************************************************************/
361extern "C" DWORD DAPI DirDeleteEmptyDirectoriesToRoot(
362 __in_z LPCWSTR wzPath,
363 __in DWORD /*dwFlags*/
364 )
365{
366 DWORD cDeletedDirs = 0;
367 LPWSTR sczPath = NULL;
368
369 while (wzPath && *wzPath && ::RemoveDirectoryW(wzPath))
370 {
371 ++cDeletedDirs;
372
373 HRESULT hr = PathGetParentPath(wzPath, &sczPath);
374 DirExitOnFailure(hr, "Failed to get parent directory for path: %ls", wzPath);
375
376 wzPath = sczPath;
377 }
378
379LExit:
380 ReleaseStr(sczPath);
381
382 return cDeletedDirs;
383}
384
385
386/*******************************************************************
387 DirGetCurrent - gets the current directory.
388
389*******************************************************************/
390extern "C" HRESULT DAPI DirGetCurrent(
391 __deref_out_z LPWSTR* psczCurrentDirectory
392 )
393{
394 HRESULT hr = S_OK;
395 SIZE_T cch = 0;
396
397 if (psczCurrentDirectory && *psczCurrentDirectory)
398 {
399 hr = StrMaxLength(*psczCurrentDirectory, &cch);
400 DirExitOnFailure(hr, "Failed to determine size of current directory.");
401 }
402
403 DWORD cchRequired = ::GetCurrentDirectoryW((DWORD)min(DWORD_MAX, cch), 0 == cch ? NULL : *psczCurrentDirectory);
404 if (0 == cchRequired)
405 {
406 DirExitWithLastError(hr, "Failed to get current directory.");
407 }
408 else if (cch < cchRequired)
409 {
410 hr = StrAlloc(psczCurrentDirectory, cchRequired);
411 DirExitOnFailure(hr, "Failed to allocate string for current directory.");
412
413 if (!::GetCurrentDirectoryW(cchRequired, *psczCurrentDirectory))
414 {
415 DirExitWithLastError(hr, "Failed to get current directory using allocated string.");
416 }
417 }
418
419LExit:
420 return hr;
421}
422
423
424/*******************************************************************
425 DirSetCurrent - sets the current directory.
426
427*******************************************************************/
428extern "C" HRESULT DAPI DirSetCurrent(
429 __in_z LPCWSTR wzDirectory
430 )
431{
432 HRESULT hr = S_OK;
433
434 if (!::SetCurrentDirectoryW(wzDirectory))
435 {
436 DirExitWithLastError(hr, "Failed to set current directory to: %ls", wzDirectory);
437 }
438
439LExit:
440 return hr;
441}
diff --git a/src/libs/dutil/WixToolset.DUtil/dlutil.cpp b/src/libs/dutil/WixToolset.DUtil/dlutil.cpp
new file mode 100644
index 00000000..70155e6f
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/dlutil.cpp
@@ -0,0 +1,802 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5#include <inetutil.h>
6#include <uriutil.h>
7
8
9// Exit macros
10#define DlExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_DLUTIL, x, s, __VA_ARGS__)
11#define DlExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_DLUTIL, x, s, __VA_ARGS__)
12#define DlExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_DLUTIL, x, s, __VA_ARGS__)
13#define DlExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_DLUTIL, x, s, __VA_ARGS__)
14#define DlExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_DLUTIL, x, s, __VA_ARGS__)
15#define DlExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_DLUTIL, x, s, __VA_ARGS__)
16#define DlExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_DLUTIL, p, x, e, s, __VA_ARGS__)
17#define DlExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_DLUTIL, p, x, s, __VA_ARGS__)
18#define DlExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_DLUTIL, p, x, e, s, __VA_ARGS__)
19#define DlExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_DLUTIL, p, x, s, __VA_ARGS__)
20#define DlExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_DLUTIL, e, x, s, __VA_ARGS__)
21#define DlExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_DLUTIL, g, x, s, __VA_ARGS__)
22
23static const DWORD64 DOWNLOAD_ENGINE_TWO_GIGABYTES = DWORD64(2) * 1024 * 1024 * 1024;
24static LPCWSTR DOWNLOAD_ENGINE_ACCEPT_TYPES[] = { L"*/*", NULL };
25
26// internal function declarations
27
28static HRESULT InitializeResume(
29 __in LPCWSTR wzDestinationPath,
30 __out LPWSTR* psczResumePath,
31 __out HANDLE* phResumeFile,
32 __out DWORD64* pdw64ResumeOffset
33 );
34static HRESULT GetResourceMetadata(
35 __in HINTERNET hSession,
36 __inout_z LPWSTR* psczUrl,
37 __in_z_opt LPCWSTR wzUser,
38 __in_z_opt LPCWSTR wzPassword,
39 __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate,
40 __out DWORD64* pdw64ResourceSize,
41 __out FILETIME* pftResourceCreated
42 );
43static HRESULT DownloadResource(
44 __in HINTERNET hSession,
45 __inout_z LPWSTR* psczUrl,
46 __in_z_opt LPCWSTR wzUser,
47 __in_z_opt LPCWSTR wzPassword,
48 __in_z LPCWSTR wzDestinationPath,
49 __in DWORD64 dw64AuthoredResourceLength,
50 __in DWORD64 dw64ResourceLength,
51 __in DWORD64 dw64ResumeOffset,
52 __in HANDLE hResumeFile,
53 __in_opt DOWNLOAD_CACHE_CALLBACK* pCache,
54 __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate
55 );
56static HRESULT AllocateRangeRequestHeader(
57 __in DWORD64 dw64ResumeOffset,
58 __in DWORD64 dw64ResourceLength,
59 __deref_inout_z LPWSTR* psczHeader
60 );
61static HRESULT WriteToFile(
62 __in HINTERNET hUrl,
63 __in HANDLE hPayloadFile,
64 __inout DWORD64* pdw64ResumeOffset,
65 __in HANDLE hResumeFile,
66 __in DWORD64 dw64ResourceLength,
67 __in LPBYTE pbData,
68 __in DWORD cbData,
69 __in_opt DOWNLOAD_CACHE_CALLBACK* pCallback
70 );
71static HRESULT UpdateResumeOffset(
72 __inout DWORD64* pdw64ResumeOffset,
73 __in HANDLE hResumeFile,
74 __in DWORD cbData
75 );
76static HRESULT MakeRequest(
77 __in HINTERNET hSession,
78 __inout_z LPWSTR* psczSourceUrl,
79 __in_z_opt LPCWSTR wzMethod,
80 __in_z_opt LPCWSTR wzHeaders,
81 __in_z_opt LPCWSTR wzUser,
82 __in_z_opt LPCWSTR wzPassword,
83 __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate,
84 __out HINTERNET* phConnect,
85 __out HINTERNET* phUrl,
86 __out BOOL* pfRangeRequestsAccepted
87 );
88static HRESULT OpenRequest(
89 __in HINTERNET hConnect,
90 __in_z_opt LPCWSTR wzMethod,
91 __in INTERNET_SCHEME scheme,
92 __in_z LPCWSTR wzResource,
93 __in_z_opt LPCWSTR wzQueryString,
94 __in_z_opt LPCWSTR wzHeader,
95 __out HINTERNET* phUrl
96 );
97static HRESULT SendRequest(
98 __in HINTERNET hUrl,
99 __inout_z LPWSTR* psczUrl,
100 __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate,
101 __out BOOL* pfRetry,
102 __out BOOL* pfRangesAccepted
103 );
104static HRESULT AuthenticationRequired(
105 __in HINTERNET hUrl,
106 __in long lHttpCode,
107 __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate,
108 __out BOOL* pfRetrySend,
109 __out BOOL* pfRetry
110 );
111static HRESULT DownloadGetResumePath(
112 __in_z LPCWSTR wzPayloadWorkingPath,
113 __deref_out_z LPWSTR* psczResumePath
114 );
115static HRESULT DownloadSendProgressCallback(
116 __in DOWNLOAD_CACHE_CALLBACK* pCallback,
117 __in DWORD64 dw64Progress,
118 __in DWORD64 dw64Total,
119 __in HANDLE hDestinationFile
120 );
121// function definitions
122
123extern "C" HRESULT DAPI DownloadUrl(
124 __in DOWNLOAD_SOURCE* pDownloadSource,
125 __in DWORD64 dw64AuthoredDownloadSize,
126 __in LPCWSTR wzDestinationPath,
127 __in_opt DOWNLOAD_CACHE_CALLBACK* pCache,
128 __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate
129 )
130{
131 HRESULT hr = S_OK;
132 LPWSTR sczUrl = NULL;
133 HINTERNET hSession = NULL;
134 DWORD dwTimeout = 0;
135 LPWSTR sczResumePath = NULL;
136 HANDLE hResumeFile = INVALID_HANDLE_VALUE;
137 DWORD64 dw64ResumeOffset = 0;
138 DWORD64 dw64Size = 0;
139 FILETIME ftCreated = { };
140
141 // Copy the download source into a working variable to handle redirects then
142 // open the internet session.
143 hr = StrAllocString(&sczUrl, pDownloadSource->sczUrl, 0);
144 DlExitOnFailure(hr, "Failed to copy download source URL.");
145
146 hSession = ::InternetOpenW(L"Burn", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
147 DlExitOnNullWithLastError(hSession, hr, "Failed to open internet session");
148
149 // Make a best effort to set the download timeouts to 2 minutes or whatever policy says.
150 PolcReadNumber(POLICY_BURN_REGISTRY_PATH, L"DownloadTimeout", 2 * 60, &dwTimeout);
151 if (0 < dwTimeout)
152 {
153 dwTimeout *= 1000; // convert to milliseconds.
154 ::InternetSetOptionW(hSession, INTERNET_OPTION_CONNECT_TIMEOUT, &dwTimeout, sizeof(dwTimeout));
155 ::InternetSetOptionW(hSession, INTERNET_OPTION_RECEIVE_TIMEOUT, &dwTimeout, sizeof(dwTimeout));
156 ::InternetSetOptionW(hSession, INTERNET_OPTION_SEND_TIMEOUT, &dwTimeout, sizeof(dwTimeout));
157 }
158
159 // Get the resource size and creation time from the internet.
160 hr = GetResourceMetadata(hSession, &sczUrl, pDownloadSource->sczUser, pDownloadSource->sczPassword, pAuthenticate, &dw64Size, &ftCreated);
161 DlExitOnFailure(hr, "Failed to get size and time for URL: %ls", sczUrl);
162
163 // Ignore failure to initialize resume because we will fall back to full download then
164 // download.
165 InitializeResume(wzDestinationPath, &sczResumePath, &hResumeFile, &dw64ResumeOffset);
166
167 hr = DownloadResource(hSession, &sczUrl, pDownloadSource->sczUser, pDownloadSource->sczPassword, wzDestinationPath, dw64AuthoredDownloadSize, dw64Size, dw64ResumeOffset, hResumeFile, pCache, pAuthenticate);
168 DlExitOnFailure(hr, "Failed to download URL: %ls", sczUrl);
169
170 // Cleanup the resume file because we successfully downloaded the whole file.
171 if (sczResumePath && *sczResumePath)
172 {
173 ::DeleteFileW(sczResumePath);
174 }
175
176LExit:
177 ReleaseFileHandle(hResumeFile);
178 ReleaseStr(sczResumePath);
179 ReleaseInternet(hSession);
180 ReleaseStr(sczUrl);
181
182 return hr;
183}
184
185
186// internal helper functions
187
188static HRESULT InitializeResume(
189 __in LPCWSTR wzDestinationPath,
190 __out LPWSTR* psczResumePath,
191 __out HANDLE* phResumeFile,
192 __out DWORD64* pdw64ResumeOffset
193 )
194{
195 HRESULT hr = S_OK;
196 HANDLE hResumeFile = INVALID_HANDLE_VALUE;
197 DWORD cbTotalReadResumeData = 0;
198 DWORD cbReadData = 0;
199
200 *pdw64ResumeOffset = 0;
201
202 hr = DownloadGetResumePath(wzDestinationPath, psczResumePath);
203 DlExitOnFailure(hr, "Failed to calculate resume path from working path: %ls", wzDestinationPath);
204
205 hResumeFile = ::CreateFileW(*psczResumePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_DELETE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
206 if (INVALID_HANDLE_VALUE == hResumeFile)
207 {
208 DlExitWithLastError(hr, "Failed to create resume file: %ls", *psczResumePath);
209 }
210
211 do
212 {
213 if (!::ReadFile(hResumeFile, reinterpret_cast<BYTE*>(pdw64ResumeOffset) + cbTotalReadResumeData, sizeof(DWORD64) - cbTotalReadResumeData, &cbReadData, NULL))
214 {
215 DlExitWithLastError(hr, "Failed to read resume file: %ls", *psczResumePath);
216 }
217 cbTotalReadResumeData += cbReadData;
218 } while (cbReadData && sizeof(DWORD64) > cbTotalReadResumeData);
219
220 // Start over if we couldn't get a resume offset.
221 if (cbTotalReadResumeData != sizeof(DWORD64))
222 {
223 *pdw64ResumeOffset = 0;
224 }
225
226 *phResumeFile = hResumeFile;
227 hResumeFile = INVALID_HANDLE_VALUE;
228
229LExit:
230 ReleaseFileHandle(hResumeFile);
231 return hr;
232}
233
234static HRESULT GetResourceMetadata(
235 __in HINTERNET hSession,
236 __inout_z LPWSTR* psczUrl,
237 __in_z_opt LPCWSTR wzUser,
238 __in_z_opt LPCWSTR wzPassword,
239 __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate,
240 __out DWORD64* pdw64ResourceSize,
241 __out FILETIME* pftResourceCreated
242 )
243{
244 HRESULT hr = S_OK;
245 BOOL fRangeRequestsAccepted = TRUE;
246 HINTERNET hConnect = NULL;
247 HINTERNET hUrl = NULL;
248 LONGLONG llLength = 0;
249
250 hr = MakeRequest(hSession, psczUrl, L"HEAD", NULL, wzUser, wzPassword, pAuthenticate, &hConnect, &hUrl, &fRangeRequestsAccepted);
251 DlExitOnFailure(hr, "Failed to connect to URL: %ls", *psczUrl);
252
253 hr = InternetGetSizeByHandle(hUrl, &llLength);
254 if (FAILED(hr))
255 {
256 llLength = 0;
257 hr = S_OK;
258 }
259
260 *pdw64ResourceSize = llLength;
261
262 // Get the last modified time from the server, we'll use that as our downloaded time here. If
263 // the server time isn't available then use the local system time.
264 hr = InternetGetCreateTimeByHandle(hUrl, pftResourceCreated);
265 if (FAILED(hr))
266 {
267 ::GetSystemTimeAsFileTime(pftResourceCreated);
268 hr = S_OK;
269 }
270
271LExit:
272 ReleaseInternet(hUrl);
273 ReleaseInternet(hConnect);
274 return hr;
275}
276
277static HRESULT DownloadResource(
278 __in HINTERNET hSession,
279 __inout_z LPWSTR* psczUrl,
280 __in_z_opt LPCWSTR wzUser,
281 __in_z_opt LPCWSTR wzPassword,
282 __in_z LPCWSTR wzDestinationPath,
283 __in DWORD64 dw64AuthoredResourceLength,
284 __in DWORD64 dw64ResourceLength,
285 __in DWORD64 dw64ResumeOffset,
286 __in HANDLE hResumeFile,
287 __in_opt DOWNLOAD_CACHE_CALLBACK* pCache,
288 __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate
289 )
290{
291 HRESULT hr = S_OK;
292 HANDLE hPayloadFile = INVALID_HANDLE_VALUE;
293 DWORD cbMaxData = 64 * 1024; // 64 KB
294 BYTE* pbData = NULL;
295 BOOL fRangeRequestsAccepted = TRUE;
296 LPWSTR sczRangeRequestHeader = NULL;
297 HINTERNET hConnect = NULL;
298 HINTERNET hUrl = NULL;
299 LONGLONG llLength = 0;
300
301 hPayloadFile = ::CreateFileW(wzDestinationPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_DELETE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
302 if (INVALID_HANDLE_VALUE == hPayloadFile)
303 {
304 DlExitWithLastError(hr, "Failed to create download destination file: %ls", wzDestinationPath);
305 }
306
307 // Allocate a memory block on a page boundary in case we want to do optimal writing.
308 pbData = static_cast<BYTE*>(::VirtualAlloc(NULL, cbMaxData, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE));
309 DlExitOnNullWithLastError(pbData, hr, "Failed to allocate buffer to download files into.");
310
311 // Let's try downloading the file assuming that range requests are accepted. If range requests
312 // are not supported we'll have to start over and accept the fact that we only get one shot
313 // downloading the file however big it is. Hopefully, not more than 2 GB since wininet doesn't
314 // like files that big.
315 while (fRangeRequestsAccepted && (0 == dw64ResourceLength || dw64ResumeOffset < dw64ResourceLength))
316 {
317 hr = AllocateRangeRequestHeader(dw64ResumeOffset, 0 == dw64ResourceLength ? dw64AuthoredResourceLength : dw64ResourceLength, &sczRangeRequestHeader);
318 DlExitOnFailure(hr, "Failed to allocate range request header.");
319
320 ReleaseNullInternet(hConnect);
321 ReleaseNullInternet(hUrl);
322
323 hr = MakeRequest(hSession, psczUrl, L"GET", sczRangeRequestHeader, wzUser, wzPassword, pAuthenticate, &hConnect, &hUrl, &fRangeRequestsAccepted);
324 DlExitOnFailure(hr, "Failed to request URL for download: %ls", *psczUrl);
325
326 // If we didn't get the size of the resource from the initial "HEAD" request
327 // then let's try to get the size from this "GET" request.
328 if (0 == dw64ResourceLength)
329 {
330 hr = InternetGetSizeByHandle(hUrl, &llLength);
331 if (SUCCEEDED(hr))
332 {
333 dw64ResourceLength = llLength;
334 }
335 else // server didn't tell us the resource length.
336 {
337 // Fallback to the authored size of the resource. However, since we
338 // don't really know the size on the server, don't try to use
339 // range requests either.
340 dw64ResourceLength = dw64AuthoredResourceLength;
341 fRangeRequestsAccepted = FALSE;
342 }
343 }
344
345 // If we just tried to do a range request and found out that it isn't supported, start over.
346 if (!fRangeRequestsAccepted)
347 {
348 // TODO: log a message that the server did not accept range requests.
349 dw64ResumeOffset = 0;
350 }
351
352 hr = WriteToFile(hUrl, hPayloadFile, &dw64ResumeOffset, hResumeFile, dw64ResourceLength, pbData, cbMaxData, pCache);
353 DlExitOnFailure(hr, "Failed while reading from internet and writing to: %ls", wzDestinationPath);
354 }
355
356LExit:
357 ReleaseInternet(hUrl);
358 ReleaseInternet(hConnect);
359 ReleaseStr(sczRangeRequestHeader);
360 if (pbData)
361 {
362 ::VirtualFree(pbData, 0, MEM_RELEASE);
363 }
364 ReleaseFileHandle(hPayloadFile);
365
366 return hr;
367}
368
369static HRESULT AllocateRangeRequestHeader(
370 __in DWORD64 dw64ResumeOffset,
371 __in DWORD64 dw64ResourceLength,
372 __deref_inout_z LPWSTR* psczHeader
373 )
374{
375 HRESULT hr = S_OK;
376
377 // If the remaining length is less that 2GB we'll be able to ask for everything.
378 DWORD64 dw64RemainingLength = dw64ResourceLength - dw64ResumeOffset;
379 if (DOWNLOAD_ENGINE_TWO_GIGABYTES > dw64RemainingLength)
380 {
381 // If we have a resume offset, let's download everything from there. Otherwise, we'll
382 // just get everything with no headers in the way.
383 if (0 < dw64ResumeOffset)
384 {
385 hr = StrAllocFormatted(psczHeader, L"Range: bytes=%I64u-", dw64ResumeOffset);
386 DlExitOnFailure(hr, "Failed to add range read header.");
387 }
388 else
389 {
390 ReleaseNullStr(*psczHeader);
391 }
392 }
393 else // we'll have to download in chunks.
394 {
395 hr = StrAllocFormatted(psczHeader, L"Range: bytes=%I64u-%I64u", dw64ResumeOffset, dw64ResumeOffset + dw64RemainingLength - 1);
396 DlExitOnFailure(hr, "Failed to add range read header.");
397 }
398
399LExit:
400 return hr;
401}
402
403static HRESULT WriteToFile(
404 __in HINTERNET hUrl,
405 __in HANDLE hPayloadFile,
406 __inout DWORD64* pdw64ResumeOffset,
407 __in HANDLE hResumeFile,
408 __in DWORD64 dw64ResourceLength,
409 __in LPBYTE pbData,
410 __in DWORD cbData,
411 __in_opt DOWNLOAD_CACHE_CALLBACK* pCallback
412 )
413{
414 HRESULT hr = S_OK;
415 DWORD cbReadData = 0;
416
417 hr = FileSetPointer(hPayloadFile, *pdw64ResumeOffset, NULL, FILE_BEGIN);
418 DlExitOnFailure(hr, "Failed to seek to start point in file.");
419
420 do
421 {
422 // Read bits from the internet.
423 if (!::InternetReadFile(hUrl, static_cast<void*>(pbData), cbData, &cbReadData))
424 {
425 DlExitWithLastError(hr, "Failed while reading from internet.");
426 }
427
428 // Write bits to disk (if there are any).
429 if (cbReadData)
430 {
431 DWORD cbTotalWritten = 0;
432 DWORD cbWritten = 0;
433 do
434 {
435 if (!::WriteFile(hPayloadFile, pbData + cbTotalWritten, cbReadData - cbTotalWritten, &cbWritten, NULL))
436 {
437 DlExitWithLastError(hr, "Failed to write data from internet.");
438 }
439
440 cbTotalWritten += cbWritten;
441 } while (cbWritten && cbTotalWritten < cbReadData);
442
443 // Ignore failure from updating resume file as this doesn't mean the download cannot succeed.
444 UpdateResumeOffset(pdw64ResumeOffset, hResumeFile, cbTotalWritten);
445
446 if (pCallback && pCallback->pfnProgress)
447 {
448 hr = DownloadSendProgressCallback(pCallback, *pdw64ResumeOffset, dw64ResourceLength, hPayloadFile);
449 DlExitOnFailure(hr, "UX aborted on cache progress.");
450 }
451 }
452 } while (cbReadData);
453
454LExit:
455 return hr;
456}
457
458static HRESULT UpdateResumeOffset(
459 __inout DWORD64* pdw64ResumeOffset,
460 __in HANDLE hResumeFile,
461 __in DWORD cbData
462 )
463{
464 HRESULT hr = S_OK;
465
466 *pdw64ResumeOffset += cbData;
467
468 if (INVALID_HANDLE_VALUE != hResumeFile)
469 {
470 DWORD cbTotalWrittenResumeData = 0;
471 DWORD cbWrittenResumeData = 0;
472
473 hr = FileSetPointer(hResumeFile, 0, NULL, FILE_BEGIN);
474 DlExitOnFailure(hr, "Failed to seek to start point in file.");
475
476 do
477 {
478 // Ignore failure to write to the resume file as that should not prevent the download from happening.
479 if (!::WriteFile(hResumeFile, pdw64ResumeOffset + cbTotalWrittenResumeData, sizeof(DWORD64) - cbTotalWrittenResumeData, &cbWrittenResumeData, NULL))
480 {
481 DlExitOnFailure(hr, "Failed to seek to write to file.");
482 }
483
484 cbTotalWrittenResumeData += cbWrittenResumeData;
485 } while (cbWrittenResumeData && sizeof(DWORD64) > cbTotalWrittenResumeData);
486 }
487
488LExit:
489 return hr;
490}
491
492static HRESULT MakeRequest(
493 __in HINTERNET hSession,
494 __inout_z LPWSTR* psczSourceUrl,
495 __in_z_opt LPCWSTR wzMethod,
496 __in_z_opt LPCWSTR wzHeaders,
497 __in_z_opt LPCWSTR wzUser,
498 __in_z_opt LPCWSTR wzPassword,
499 __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate,
500 __out HINTERNET* phConnect,
501 __out HINTERNET* phUrl,
502 __out BOOL* pfRangeRequestsAccepted
503 )
504{
505 HRESULT hr = S_OK;
506 HINTERNET hConnect = NULL;
507 HINTERNET hUrl = NULL;
508 URI_INFO uri = { };
509
510 // Try to open the URL.
511 BOOL fRetry;
512 do
513 {
514 fRetry = FALSE;
515
516 // If the URL was opened close it, so we can reopen it again.
517 ReleaseInternet(hUrl);
518 ReleaseInternet(hConnect);
519
520 // Open the url.
521 hr = UriCrackEx(*psczSourceUrl, &uri);
522 DlExitOnFailure(hr, "Failed to break URL into server and resource parts.");
523
524 hConnect = ::InternetConnectW(hSession, uri.sczHostName, uri.port, (wzUser && *wzUser) ? wzUser : uri.sczUser, (wzPassword && *wzPassword) ? wzPassword : uri.sczPassword, INTERNET_SCHEME_FTP == uri.scheme ? INTERNET_SERVICE_FTP : INTERNET_SERVICE_HTTP, 0, 0);
525 DlExitOnNullWithLastError(hConnect, hr, "Failed to connect to URL: %ls", *psczSourceUrl);
526
527 // Best effort set the proxy username and password, if they were provided.
528 if ((wzUser && *wzUser) && (wzPassword && *wzPassword))
529 {
530 if (::InternetSetOptionW(hConnect, INTERNET_OPTION_PROXY_USERNAME, (LPVOID)wzUser, lstrlenW(wzUser)))
531 {
532 ::InternetSetOptionW(hConnect, INTERNET_OPTION_PROXY_PASSWORD, (LPVOID)wzPassword, lstrlenW(wzPassword));
533 }
534 }
535
536 hr = OpenRequest(hConnect, wzMethod, uri.scheme, uri.sczPath, uri.sczQueryString, wzHeaders, &hUrl);
537 DlExitOnFailure(hr, "Failed to open internet URL: %ls", *psczSourceUrl);
538
539 hr = SendRequest(hUrl, psczSourceUrl, pAuthenticate, &fRetry, pfRangeRequestsAccepted);
540 DlExitOnFailure(hr, "Failed to send request to URL: %ls", *psczSourceUrl);
541 } while (fRetry);
542
543 // Okay, we're all ready to start downloading. Update the connection information.
544 *phConnect = hConnect;
545 hConnect = NULL;
546 *phUrl = hUrl;
547 hUrl = NULL;
548
549LExit:
550 UriInfoUninitialize(&uri);
551 ReleaseInternet(hUrl);
552 ReleaseInternet(hConnect);
553
554 return hr;
555}
556
557static HRESULT OpenRequest(
558 __in HINTERNET hConnect,
559 __in_z_opt LPCWSTR wzMethod,
560 __in INTERNET_SCHEME scheme,
561 __in_z LPCWSTR wzResource,
562 __in_z_opt LPCWSTR wzQueryString,
563 __in_z_opt LPCWSTR wzHeader,
564 __out HINTERNET* phUrl
565 )
566{
567 HRESULT hr = S_OK;
568 DWORD dwRequestFlags = INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_NO_UI | INTERNET_FLAG_RELOAD;
569 LPWSTR sczResource = NULL;
570 HINTERNET hUrl = NULL;
571
572 if (INTERNET_SCHEME_HTTPS == scheme)
573 {
574 dwRequestFlags |= INTERNET_FLAG_SECURE;
575 }
576 else if (INTERNET_SCHEME_HTTP == scheme)
577 {
578 dwRequestFlags |= INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS;
579 }
580
581 // Allocate the resource name.
582 hr = StrAllocString(&sczResource, wzResource, 0);
583 DlExitOnFailure(hr, "Failed to allocate string for resource URI.");
584
585 if (wzQueryString && *wzQueryString)
586 {
587 hr = StrAllocConcat(&sczResource, wzQueryString, 0);
588 DlExitOnFailure(hr, "Failed to append query strong to resource from URI.");
589 }
590
591 // Open the request and add the header if provided.
592 hUrl = ::HttpOpenRequestW(hConnect, wzMethod, sczResource, NULL, NULL, DOWNLOAD_ENGINE_ACCEPT_TYPES, dwRequestFlags, NULL);
593 DlExitOnNullWithLastError(hUrl, hr, "Failed to open internet request.");
594
595 if (wzHeader && *wzHeader)
596 {
597 if (!::HttpAddRequestHeadersW(hUrl, wzHeader, static_cast<DWORD>(-1), HTTP_ADDREQ_FLAG_COALESCE))
598 {
599 DlExitWithLastError(hr, "Failed to add header to HTTP request.");
600 }
601 }
602
603 *phUrl = hUrl;
604 hUrl = NULL;
605
606LExit:
607 ReleaseInternet(hUrl);
608 ReleaseStr(sczResource);
609 return hr;
610}
611
612static HRESULT SendRequest(
613 __in HINTERNET hUrl,
614 __inout_z LPWSTR* psczUrl,
615 __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate,
616 __out BOOL* pfRetry,
617 __out BOOL* pfRangesAccepted
618 )
619{
620 HRESULT hr = S_OK;
621 BOOL fRetrySend = FALSE;
622 LONG lCode = 0;
623
624 do
625 {
626 fRetrySend = FALSE;
627
628 if (!::HttpSendRequestW(hUrl, NULL, 0, NULL, 0))
629 {
630 hr = HRESULT_FROM_WIN32(::GetLastError()); // remember the error that occurred and log it.
631 LogErrorString(hr, "Failed to send request to URL: %ls, trying to process HTTP status code anyway.", *psczUrl);
632
633 // Try to get the HTTP status code and, if good, handle via the switch statement below but if it
634 // fails return the error code from the send request above as the result of the function.
635 HRESULT hrQueryStatusCode = InternetQueryInfoNumber(hUrl, HTTP_QUERY_STATUS_CODE, &lCode);
636 DlExitOnFailure(hrQueryStatusCode, "Failed to get HTTP status code for failed request to URL: %ls", *psczUrl);
637 }
638 else // get the http status code.
639 {
640 hr = InternetQueryInfoNumber(hUrl, HTTP_QUERY_STATUS_CODE, &lCode);
641 DlExitOnFailure(hr, "Failed to get HTTP status code for request to URL: %ls", *psczUrl);
642 }
643
644 switch (lCode)
645 {
646 case 200: // OK but range requests don't work.
647 *pfRangesAccepted = FALSE;
648 hr = S_OK;
649 break;
650
651 case 206: // Partial content means that range requests work!
652 *pfRangesAccepted = TRUE;
653 hr = S_OK;
654 break;
655
656 // redirection cases
657 case 301: __fallthrough; // file moved
658 case 302: __fallthrough; // temporary
659 case 303: // redirect method
660 hr = InternetQueryInfoString(hUrl, HTTP_QUERY_CONTENT_LOCATION, psczUrl);
661 DlExitOnFailure(hr, "Failed to get redirect url: %ls", *psczUrl);
662
663 *pfRetry = TRUE;
664 break;
665
666 // error cases
667 case 400: // bad request
668 hr = HRESULT_FROM_WIN32(ERROR_BAD_PATHNAME);
669 break;
670
671 case 401: __fallthrough; // unauthorized
672 case 407: __fallthrough; // proxy unauthorized
673 hr = AuthenticationRequired(hUrl, lCode, pAuthenticate, &fRetrySend, pfRetry);
674 break;
675
676 case 403: // forbidden
677 hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
678 break;
679
680 case 404: // file not found
681 case 410: // gone
682 hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
683 break;
684
685 case 405: // method not allowed
686 hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
687 break;
688
689 case 408: __fallthrough; // request timedout
690 case 504: // gateway timeout
691 hr = HRESULT_FROM_WIN32(WAIT_TIMEOUT);
692 break;
693
694 case 414: // request URI too long
695 hr = CO_E_PATHTOOLONG;
696 break;
697
698 case 502: __fallthrough; // server (through a gateway) was not found
699 case 503: // server unavailable
700 hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
701 break;
702
703 case 418: // I'm a teapot.
704 default:
705 // If the request failed and the HTTP status code was invalid (but wininet gave us a number anyway)
706 // do not overwrite the error code from the failed request. Otherwise, the error was unexpected.
707 if (SUCCEEDED(hr))
708 {
709 hr = E_UNEXPECTED;
710 }
711
712 LogErrorString(hr, "Unknown HTTP status code %d, returned from URL: %ls", lCode, *psczUrl);
713 break;
714 }
715 } while (fRetrySend);
716
717LExit:
718 return hr;
719}
720
721static HRESULT AuthenticationRequired(
722 __in HINTERNET hUrl,
723 __in long lHttpCode,
724 __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate,
725 __out BOOL* pfRetrySend,
726 __out BOOL* pfRetry
727 )
728{
729 Assert(401 == lHttpCode || 407 == lHttpCode);
730
731 HRESULT hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
732 *pfRetrySend = FALSE;
733 *pfRetry = FALSE;
734
735 if (pAuthenticate && pAuthenticate->pfnAuthenticate)
736 {
737 hr = (*pAuthenticate->pfnAuthenticate)(pAuthenticate->pv, hUrl, lHttpCode, pfRetrySend, pfRetry);
738 }
739
740 return hr;
741}
742
743
744static HRESULT DownloadGetResumePath(
745 __in_z LPCWSTR wzPayloadWorkingPath,
746 __deref_out_z LPWSTR* psczResumePath
747 )
748{
749 HRESULT hr = S_OK;
750
751 hr = StrAllocFormatted(psczResumePath, L"%ls.R", wzPayloadWorkingPath);
752 DlExitOnFailure(hr, "Failed to create resume path.");
753
754LExit:
755 return hr;
756}
757
758static HRESULT DownloadSendProgressCallback(
759 __in DOWNLOAD_CACHE_CALLBACK* pCallback,
760 __in DWORD64 dw64Progress,
761 __in DWORD64 dw64Total,
762 __in HANDLE hDestinationFile
763 )
764{
765 static LARGE_INTEGER LARGE_INTEGER_ZERO = { };
766
767 HRESULT hr = S_OK;
768 DWORD dwResult = PROGRESS_CONTINUE;
769 LARGE_INTEGER liTotalSize = { };
770 LARGE_INTEGER liTotalTransferred = { };
771
772 if (pCallback->pfnProgress)
773 {
774 liTotalSize.QuadPart = dw64Total;
775 liTotalTransferred.QuadPart = dw64Progress;
776
777 dwResult = (*pCallback->pfnProgress)(liTotalSize, liTotalTransferred, LARGE_INTEGER_ZERO, LARGE_INTEGER_ZERO, 1, CALLBACK_CHUNK_FINISHED, INVALID_HANDLE_VALUE, hDestinationFile, pCallback->pv);
778 switch (dwResult)
779 {
780 case PROGRESS_CONTINUE:
781 hr = S_OK;
782 break;
783
784 case PROGRESS_CANCEL: __fallthrough; // TODO: should cancel and stop be treated differently?
785 case PROGRESS_STOP:
786 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
787 DlExitOnRootFailure(hr, "UX aborted on download progress.");
788
789 case PROGRESS_QUIET: // Not actually an error, just an indication to the caller to stop requesting progress.
790 pCallback->pfnProgress = NULL;
791 hr = S_OK;
792 break;
793
794 default:
795 hr = E_UNEXPECTED;
796 DlExitOnRootFailure(hr, "Invalid return code from progress routine.");
797 }
798 }
799
800LExit:
801 return hr;
802}
diff --git a/src/libs/dutil/WixToolset.DUtil/dpiutil.cpp b/src/libs/dutil/WixToolset.DUtil/dpiutil.cpp
new file mode 100644
index 00000000..4096c8d3
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/dpiutil.cpp
@@ -0,0 +1,274 @@
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// Exit macros
6#define DpiuExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_DPIUTIL, x, s, __VA_ARGS__)
7#define DpiuExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_DPIUTIL, x, s, __VA_ARGS__)
8#define DpiuExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_DPIUTIL, x, s, __VA_ARGS__)
9#define DpiuExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_DPIUTIL, x, s, __VA_ARGS__)
10#define DpiuExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_DPIUTIL, x, s, __VA_ARGS__)
11#define DpiuExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_DPIUTIL, x, s, __VA_ARGS__)
12#define DpiuExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_DPIUTIL, p, x, e, s, __VA_ARGS__)
13#define DpiuExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_DPIUTIL, p, x, s, __VA_ARGS__)
14#define DpiuExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_DPIUTIL, p, x, e, s, __VA_ARGS__)
15#define DpiuExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_DPIUTIL, p, x, s, __VA_ARGS__)
16#define DpiuExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_DPIUTIL, e, x, s, __VA_ARGS__)
17
18static PFN_ADJUSTWINDOWRECTEXFORDPI vpfnAdjustWindowRectExForDpi = NULL;
19static PFN_GETDPIFORMONITOR vpfnGetDpiForMonitor = NULL;
20static PFN_GETDPIFORWINDOW vpfnGetDpiForWindow = NULL;
21static PFN_SETPROCESSDPIAWARE vpfnSetProcessDPIAware = NULL;
22static PFN_SETPROCESSDPIAWARENESS vpfnSetProcessDpiAwareness = NULL;
23static PFN_SETPROCESSDPIAWARENESSCONTEXT vpfnSetProcessDpiAwarenessContext = NULL;
24
25static HMODULE vhShcoreDll = NULL;
26static HMODULE vhUser32Dll = NULL;
27static BOOL vfDpiuInitialized = FALSE;
28
29DAPI_(void) DpiuInitialize()
30{
31 HRESULT hr = S_OK;
32
33 hr = LoadSystemLibrary(L"Shcore.dll", &vhShcoreDll);
34 if (SUCCEEDED(hr))
35 {
36 // Ignore failures.
37 vpfnGetDpiForMonitor = reinterpret_cast<PFN_GETDPIFORMONITOR>(::GetProcAddress(vhShcoreDll, "GetDpiForMonitor"));
38 vpfnSetProcessDpiAwareness = reinterpret_cast<PFN_SETPROCESSDPIAWARENESS>(::GetProcAddress(vhShcoreDll, "SetProcessDpiAwareness"));
39 }
40
41 hr = LoadSystemLibrary(L"User32.dll", &vhUser32Dll);
42 if (SUCCEEDED(hr))
43 {
44 // Ignore failures.
45 vpfnAdjustWindowRectExForDpi = reinterpret_cast<PFN_ADJUSTWINDOWRECTEXFORDPI>(::GetProcAddress(vhUser32Dll, "AdjustWindowRectExForDpi"));
46 vpfnGetDpiForWindow = reinterpret_cast<PFN_GETDPIFORWINDOW>(::GetProcAddress(vhUser32Dll, "GetDpiForWindow"));
47 vpfnSetProcessDPIAware = reinterpret_cast<PFN_SETPROCESSDPIAWARE>(::GetProcAddress(vhUser32Dll, "SetProcessDPIAware"));
48 vpfnSetProcessDpiAwarenessContext = reinterpret_cast<PFN_SETPROCESSDPIAWARENESSCONTEXT>(::GetProcAddress(vhUser32Dll, "SetProcessDpiAwarenessContext"));
49 }
50
51 vfDpiuInitialized = TRUE;
52}
53
54DAPI_(void) DpiuUninitialize()
55{
56 if (vhShcoreDll)
57 {
58 ::FreeLibrary(vhShcoreDll);
59 }
60
61 if (vhUser32Dll)
62 {
63 ::FreeLibrary(vhUser32Dll);
64 }
65
66 vhShcoreDll = NULL;
67 vhUser32Dll = NULL;
68 vpfnAdjustWindowRectExForDpi = NULL;
69 vpfnGetDpiForMonitor = NULL;
70 vpfnGetDpiForWindow = NULL;
71 vfDpiuInitialized = FALSE;
72}
73
74DAPI_(void) DpiuAdjustWindowRect(
75 __in RECT* pWindowRect,
76 __in DWORD dwStyle,
77 __in BOOL fMenu,
78 __in DWORD dwExStyle,
79 __in UINT nDpi
80 )
81{
82 if (WS_SYSMENU & dwStyle)
83 {
84 dwStyle |= WS_CAPTION; // WS_CAPTION is required with WS_SYSMENU, AdjustWindowRect* won't work properly when it's not specified.
85 }
86
87 if (vpfnAdjustWindowRectExForDpi)
88 {
89 vpfnAdjustWindowRectExForDpi(pWindowRect, dwStyle, fMenu, dwExStyle, nDpi);
90 }
91 else
92 {
93 ::AdjustWindowRectEx(pWindowRect, dwStyle, fMenu, dwExStyle);
94 }
95}
96
97DAPI_(HRESULT) DpiuGetMonitorContextFromPoint(
98 __in const POINT* pt,
99 __out DPIU_MONITOR_CONTEXT** ppMonitorContext
100 )
101{
102 HRESULT hr = S_OK;
103 DPIU_MONITOR_CONTEXT* pMonitorContext = NULL;
104 HMONITOR hMonitor = NULL;
105 UINT dpiX = 0;
106 UINT dpiY = 0;
107 HDC hdc = NULL;
108
109 pMonitorContext = reinterpret_cast<DPIU_MONITOR_CONTEXT*>(MemAlloc(sizeof(DPIU_MONITOR_CONTEXT), TRUE));
110 DpiuExitOnNull(pMonitorContext, hr, E_OUTOFMEMORY, "Failed to allocate memory for DpiuMonitorContext.");
111
112 hMonitor = ::MonitorFromPoint(*pt, MONITOR_DEFAULTTONEAREST);
113 DpiuExitOnNull(hMonitor, hr, E_FAIL, "Failed to get monitor from point.");
114
115 pMonitorContext->mi.cbSize = sizeof(pMonitorContext->mi);
116 if (!::GetMonitorInfoW(hMonitor, &pMonitorContext->mi))
117 {
118 DpiuExitOnFailure(hr = E_OUTOFMEMORY, "Failed to get monitor info for point.");
119 }
120
121 if (vpfnGetDpiForMonitor)
122 {
123 hr = vpfnGetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY);
124 DpiuExitOnFailure(hr, "Failed to get DPI for monitor.");
125
126 pMonitorContext->nDpi = dpiX;
127 }
128 else
129 {
130 hdc = ::CreateDCW(L"DISPLAY", pMonitorContext->mi.szDevice, NULL, NULL);
131 DpiuExitOnNull(hdc, hr, E_OUTOFMEMORY, "Failed to get device context for monitor.");
132
133 pMonitorContext->nDpi = ::GetDeviceCaps(hdc, LOGPIXELSX);
134 }
135
136 *ppMonitorContext = pMonitorContext;
137 pMonitorContext = NULL;
138
139LExit:
140 if (hdc)
141 {
142 ::ReleaseDC(NULL, hdc);
143 }
144
145 MemFree(pMonitorContext);
146
147 return hr;
148}
149
150DAPI_(void) DpiuGetWindowContext(
151 __in HWND hWnd,
152 __in DPIU_WINDOW_CONTEXT* pWindowContext
153 )
154{
155 HRESULT hr = S_OK;
156 HMONITOR hMonitor = NULL;
157 UINT dpiX = 0;
158 UINT dpiY = 0;
159 HDC hdc = NULL;
160
161 pWindowContext->nDpi = USER_DEFAULT_SCREEN_DPI;
162
163 if (vpfnGetDpiForWindow)
164 {
165 pWindowContext->nDpi = vpfnGetDpiForWindow(hWnd);
166 ExitFunction();
167 }
168
169 if (vpfnGetDpiForMonitor)
170 {
171 hMonitor = ::MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST);
172 if (hMonitor)
173 {
174 hr = vpfnGetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY);
175 if (SUCCEEDED(hr))
176 {
177 pWindowContext->nDpi = dpiX;
178 ExitFunction();
179 }
180 }
181 }
182
183 hdc = ::GetDC(hWnd);
184 if (hdc)
185 {
186 pWindowContext->nDpi = ::GetDeviceCaps(hdc, LOGPIXELSX);
187 }
188
189LExit:
190 if (hdc)
191 {
192 ::ReleaseDC(hWnd, hdc);
193 }
194}
195
196DAPI_(int) DpiuScaleValue(
197 __in int nDefaultDpiValue,
198 __in UINT nTargetDpi
199 )
200{
201 return ::MulDiv(nDefaultDpiValue, nTargetDpi, USER_DEFAULT_SCREEN_DPI);
202}
203
204DAPI_(HRESULT) DpiuSetProcessDpiAwareness(
205 __in DPIU_AWARENESS supportedAwareness,
206 __in_opt DPIU_AWARENESS* pSelectedAwareness
207 )
208{
209 HRESULT hr = S_OK;
210 DPIU_AWARENESS selectedAwareness = DPIU_AWARENESS_NONE;
211 DPI_AWARENESS_CONTEXT awarenessContext = DPI_AWARENESS_CONTEXT_UNAWARE;
212 PROCESS_DPI_AWARENESS awareness = PROCESS_DPI_UNAWARE;
213
214 if (vpfnSetProcessDpiAwarenessContext)
215 {
216 if (DPIU_AWARENESS_PERMONITORV2 & supportedAwareness)
217 {
218 awarenessContext = DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2;
219 selectedAwareness = DPIU_AWARENESS_PERMONITORV2;
220 }
221 else if (DPIU_AWARENESS_PERMONITOR & supportedAwareness)
222 {
223 awarenessContext = DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE;
224 selectedAwareness = DPIU_AWARENESS_PERMONITOR;
225 }
226 else if (DPIU_AWARENESS_SYSTEM & supportedAwareness)
227 {
228 awarenessContext = DPI_AWARENESS_CONTEXT_SYSTEM_AWARE;
229 selectedAwareness = DPIU_AWARENESS_SYSTEM;
230 }
231 else if (DPIU_AWARENESS_GDISCALED & supportedAwareness)
232 {
233 awarenessContext = DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED;
234 selectedAwareness = DPIU_AWARENESS_GDISCALED;
235 }
236
237 if (!vpfnSetProcessDpiAwarenessContext(awarenessContext))
238 {
239 DpiuExitOnLastError(hr, "Failed to set process DPI awareness context.");
240 }
241 }
242 else if (vpfnSetProcessDpiAwareness)
243 {
244 if (DPIU_AWARENESS_PERMONITOR & supportedAwareness)
245 {
246 awareness = PROCESS_PER_MONITOR_DPI_AWARE;
247 selectedAwareness = DPIU_AWARENESS_PERMONITOR;
248 }
249 else if (DPIU_AWARENESS_SYSTEM & supportedAwareness)
250 {
251 awareness = PROCESS_SYSTEM_DPI_AWARE;
252 selectedAwareness = DPIU_AWARENESS_SYSTEM;
253 }
254
255 hr = vpfnSetProcessDpiAwareness(awareness);
256 DpiuExitOnFailure(hr, "Failed to set process DPI awareness.");
257 }
258 else if (vpfnSetProcessDPIAware && (DPIU_AWARENESS_SYSTEM & supportedAwareness))
259 {
260 selectedAwareness = DPIU_AWARENESS_SYSTEM;
261 if (!vpfnSetProcessDPIAware())
262 {
263 DpiuExitOnLastError(hr, "Failed to set process DPI aware.");
264 }
265 }
266
267LExit:
268 if (pSelectedAwareness)
269 {
270 *pSelectedAwareness = selectedAwareness;
271 }
272
273 return hr;
274}
diff --git a/src/libs/dutil/WixToolset.DUtil/dutil.cpp b/src/libs/dutil/WixToolset.DUtil/dutil.cpp
new file mode 100644
index 00000000..56b85207
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/dutil.cpp
@@ -0,0 +1,524 @@
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// Exit macros
7#define DExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_DUTIL, x, s, __VA_ARGS__)
8#define DExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_DUTIL, x, s, __VA_ARGS__)
9#define DExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_DUTIL, x, s, __VA_ARGS__)
10#define DExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_DUTIL, x, s, __VA_ARGS__)
11#define DExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_DUTIL, x, s, __VA_ARGS__)
12#define DExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_DUTIL, x, s, __VA_ARGS__)
13#define DExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_DUTIL, p, x, e, s, __VA_ARGS__)
14#define DExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_DUTIL, p, x, s, __VA_ARGS__)
15#define DExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_DUTIL, p, x, e, s, __VA_ARGS__)
16#define DExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_DUTIL, p, x, s, __VA_ARGS__)
17#define DExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_DUTIL, e, x, s, __VA_ARGS__)
18#define DExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_DUTIL, g, x, s, __VA_ARGS__)
19
20// No need for OACR to warn us about using non-unicode APIs in this file.
21#pragma prefast(disable:25068)
22
23// Asserts & Tracing
24
25const int DUTIL_STRING_BUFFER = 1024;
26static HMODULE Dutil_hAssertModule = NULL;
27static DUTIL_ASSERTDISPLAYFUNCTION Dutil_pfnDisplayAssert = NULL;
28static BOOL Dutil_fNoAsserts = FALSE;
29static REPORT_LEVEL Dutil_rlCurrentTrace = REPORT_STANDARD;
30static BOOL Dutil_fTraceFilenames = FALSE;
31static DUTIL_CALLBACK_TRACEERROR vpfnTraceErrorCallback = NULL;
32
33
34DAPI_(HRESULT) DutilInitialize(
35 __in_opt DUTIL_CALLBACK_TRACEERROR pfnTraceErrorCallback
36 )
37{
38 HRESULT hr = S_OK;
39
40 vpfnTraceErrorCallback = pfnTraceErrorCallback;
41
42 return hr;
43}
44
45
46DAPI_(void) DutilUninitialize()
47{
48 vpfnTraceErrorCallback = NULL;
49}
50
51/*******************************************************************
52Dutil_SetAssertModule
53
54*******************************************************************/
55extern "C" void DAPI Dutil_SetAssertModule(
56 __in HMODULE hAssertModule
57 )
58{
59 Dutil_hAssertModule = hAssertModule;
60}
61
62
63/*******************************************************************
64Dutil_SetAssertDisplayFunction
65
66*******************************************************************/
67extern "C" void DAPI Dutil_SetAssertDisplayFunction(
68 __in DUTIL_ASSERTDISPLAYFUNCTION pfn
69 )
70{
71 Dutil_pfnDisplayAssert = pfn;
72}
73
74
75/*******************************************************************
76Dutil_AssertMsg
77
78*******************************************************************/
79extern "C" void DAPI Dutil_AssertMsg(
80 __in_z LPCSTR szMessage
81 )
82{
83 static BOOL fInAssert = FALSE; // TODO: make this thread safe (this is a cheap hack to prevent re-entrant Asserts)
84
85 HRESULT hr = S_OK;
86 DWORD er = ERROR_SUCCESS;
87
88 int id = IDRETRY;
89 HKEY hkDebug = NULL;
90 HANDLE hAssertFile = INVALID_HANDLE_VALUE;
91 char szPath[MAX_PATH] = { };
92 DWORD cch = 0;
93
94 if (fInAssert)
95 {
96 return;
97 }
98 fInAssert = TRUE;
99
100 char szMsg[DUTIL_STRING_BUFFER];
101 hr = ::StringCchCopyA(szMsg, countof(szMsg), szMessage);
102 DExitOnFailure(hr, "failed to copy message while building assert message");
103
104 if (Dutil_pfnDisplayAssert)
105 {
106 // call custom function to display the assert string
107 if (!Dutil_pfnDisplayAssert(szMsg))
108 {
109 ExitFunction();
110 }
111 }
112 else
113 {
114 OutputDebugStringA(szMsg);
115 }
116
117 if (!Dutil_fNoAsserts)
118 {
119 er = ::RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Delivery\\Debug", 0, KEY_QUERY_VALUE, &hkDebug);
120 if (ERROR_SUCCESS == er)
121 {
122 cch = countof(szPath);
123 er = ::RegQueryValueExA(hkDebug, "DeliveryAssertsLog", NULL, NULL, reinterpret_cast<BYTE*>(szPath), &cch);
124 szPath[countof(szPath) - 1] = '\0'; // ensure string is null terminated since registry won't guarantee that.
125 if (ERROR_SUCCESS == er)
126 {
127 hAssertFile = ::CreateFileA(szPath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
128 if (INVALID_HANDLE_VALUE != hAssertFile)
129 {
130 if (INVALID_SET_FILE_POINTER != ::SetFilePointer(hAssertFile, 0, 0, FILE_END))
131 {
132 if (SUCCEEDED(::StringCchCatA(szMsg, countof(szMsg), "\r\n")))
133 {
134 ::WriteFile(hAssertFile, szMsg, lstrlenA(szMsg), &cch, NULL);
135 }
136 }
137 }
138 }
139 }
140
141 // if anything went wrong while fooling around with the registry, just show the usual assert dialog box
142 if (ERROR_SUCCESS != er)
143 {
144 hr = ::StringCchCatA(szMsg, countof(szMsg), "\nAbort=Debug, Retry=Skip, Ignore=Skip all");
145 DExitOnFailure(hr, "failed to concat string while building assert message");
146
147 id = ::MessageBoxA(0, szMsg, "Debug Assert Message",
148 MB_SERVICE_NOTIFICATION | MB_TOPMOST |
149 MB_DEFBUTTON2 | MB_ABORTRETRYIGNORE);
150 }
151 }
152
153 if (id == IDABORT)
154 {
155 if (Dutil_hAssertModule)
156 {
157 ::GetModuleFileNameA(Dutil_hAssertModule, szPath, countof(szPath));
158
159 hr = ::StringCchPrintfA(szMsg, countof(szMsg), "Module is running from: %s\nIf you are not using pdb-stamping, place your PDB near the module and attach to process id: %d (0x%x)", szPath, ::GetCurrentProcessId(), ::GetCurrentProcessId());
160 if (SUCCEEDED(hr))
161 {
162 ::MessageBoxA(0, szMsg, "Debug Assert Message", MB_SERVICE_NOTIFICATION | MB_TOPMOST | MB_OK);
163 }
164 }
165
166 ::DebugBreak();
167 }
168 else if (id == IDIGNORE)
169 {
170 Dutil_fNoAsserts = TRUE;
171 }
172
173LExit:
174 ReleaseFileHandle(hAssertFile);
175 ReleaseRegKey(hkDebug);
176 fInAssert = FALSE;
177}
178
179
180/*******************************************************************
181Dutil_Assert
182
183*******************************************************************/
184extern "C" void DAPI Dutil_Assert(
185 __in_z LPCSTR szFile,
186 __in int iLine
187 )
188{
189 HRESULT hr = S_OK;
190 char szMessage[DUTIL_STRING_BUFFER] = { };
191 hr = ::StringCchPrintfA(szMessage, countof(szMessage), "Assertion failed in %s, %i", szFile, iLine);
192 if (SUCCEEDED(hr))
193 {
194 Dutil_AssertMsg(szMessage);
195 }
196 else
197 {
198 Dutil_AssertMsg("Assert failed to build string");
199 }
200}
201
202
203/*******************************************************************
204Dutil_AssertSz
205
206*******************************************************************/
207extern "C" void DAPI Dutil_AssertSz(
208 __in_z LPCSTR szFile,
209 __in int iLine,
210 __in_z __format_string LPCSTR szMsg
211 )
212{
213 HRESULT hr = S_OK;
214 char szMessage[DUTIL_STRING_BUFFER] = { };
215
216 hr = ::StringCchPrintfA(szMessage, countof(szMessage), "Assertion failed in %s, %i\n%s", szFile, iLine, szMsg);
217 if (SUCCEEDED(hr))
218 {
219 Dutil_AssertMsg(szMessage);
220 }
221 else
222 {
223 Dutil_AssertMsg("Assert failed to build string");
224 }
225}
226
227
228/*******************************************************************
229Dutil_TraceSetLevel
230
231*******************************************************************/
232extern "C" void DAPI Dutil_TraceSetLevel(
233 __in REPORT_LEVEL rl,
234 __in BOOL fTraceFilenames
235 )
236{
237 Dutil_rlCurrentTrace = rl;
238 Dutil_fTraceFilenames = fTraceFilenames;
239}
240
241
242/*******************************************************************
243Dutil_TraceGetLevel
244
245*******************************************************************/
246extern "C" REPORT_LEVEL DAPI Dutil_TraceGetLevel()
247{
248 return Dutil_rlCurrentTrace;
249}
250
251
252/*******************************************************************
253Dutil_Trace
254
255*******************************************************************/
256extern "C" void DAPIV Dutil_Trace(
257 __in_z LPCSTR szFile,
258 __in int iLine,
259 __in REPORT_LEVEL rl,
260 __in_z __format_string LPCSTR szFormat,
261 ...
262 )
263{
264 AssertSz(REPORT_NONE != rl, "REPORT_NONE is not a valid tracing level");
265
266 HRESULT hr = S_OK;
267 char szOutput[DUTIL_STRING_BUFFER] = { };
268 char szMsg[DUTIL_STRING_BUFFER] = { };
269
270 if (Dutil_rlCurrentTrace < rl)
271 {
272 return;
273 }
274
275 va_list args;
276 va_start(args, szFormat);
277 hr = ::StringCchVPrintfA(szOutput, countof(szOutput), szFormat, args);
278 va_end(args);
279
280 if (SUCCEEDED(hr))
281 {
282 LPCSTR szPrefix = "Trace/u";
283 switch (rl)
284 {
285 case REPORT_STANDARD:
286 szPrefix = "Trace/s";
287 break;
288 case REPORT_VERBOSE:
289 szPrefix = "Trace/v";
290 break;
291 case REPORT_DEBUG:
292 szPrefix = "Trace/d";
293 break;
294 }
295
296 if (Dutil_fTraceFilenames)
297 {
298 hr = ::StringCchPrintfA(szMsg, countof(szMsg), "%s [%s,%d]: %s\r\n", szPrefix, szFile, iLine, szOutput);
299 }
300 else
301 {
302 hr = ::StringCchPrintfA(szMsg, countof(szMsg), "%s: %s\r\n", szPrefix, szOutput);
303 }
304
305 if (SUCCEEDED(hr))
306 {
307 OutputDebugStringA(szMsg);
308 }
309 // else fall through to the case below
310 }
311
312 if (FAILED(hr))
313 {
314 if (Dutil_fTraceFilenames)
315 {
316 ::StringCchPrintfA(szMsg, countof(szMsg), "Trace [%s,%d]: message too long, skipping\r\n", szFile, iLine);
317 }
318 else
319 {
320 ::StringCchPrintfA(szMsg, countof(szMsg), "Trace: message too long, skipping\r\n");
321 }
322
323 szMsg[countof(szMsg)-1] = '\0';
324 OutputDebugStringA(szMsg);
325 }
326}
327
328
329/*******************************************************************
330Dutil_TraceError
331
332*******************************************************************/
333extern "C" void DAPIV Dutil_TraceError(
334 __in_z LPCSTR szFile,
335 __in int iLine,
336 __in REPORT_LEVEL rl,
337 __in HRESULT hrError,
338 __in_z __format_string LPCSTR szFormat,
339 ...
340 )
341{
342 HRESULT hr = S_OK;
343 char szOutput[DUTIL_STRING_BUFFER] = { };
344 char szMsg[DUTIL_STRING_BUFFER] = { };
345
346 // if this is NOT an error report and we're not logging at this level, bail
347 if (REPORT_ERROR != rl && Dutil_rlCurrentTrace < rl)
348 {
349 return;
350 }
351
352 va_list args;
353 va_start(args, szFormat);
354 hr = ::StringCchVPrintfA(szOutput, countof(szOutput), szFormat, args);
355 va_end(args);
356
357 if (SUCCEEDED(hr))
358 {
359 if (Dutil_fTraceFilenames)
360 {
361 if (FAILED(hrError))
362 {
363 hr = ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError 0x%x [%s,%d]: %s\r\n", hrError, szFile, iLine, szOutput);
364 }
365 else
366 {
367 hr = ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError [%s,%d]: %s\r\n", szFile, iLine, szOutput);
368 }
369 }
370 else
371 {
372 if (FAILED(hrError))
373 {
374 hr = ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError 0x%x: %s\r\n", hrError, szOutput);
375 }
376 else
377 {
378 hr = ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError: %s\r\n", szOutput);
379 }
380 }
381
382 if (SUCCEEDED(hr))
383 {
384 OutputDebugStringA(szMsg);
385 }
386 // else fall through to the failure case below
387 }
388
389 if (FAILED(hr))
390 {
391 if (Dutil_fTraceFilenames)
392 {
393 if (FAILED(hrError))
394 {
395 ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError 0x%x [%s,%d]: message too long, skipping\r\n", hrError, szFile, iLine);
396 }
397 else
398 {
399 ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError [%s,%d]: message too long, skipping\r\n", szFile, iLine);
400 }
401 }
402 else
403 {
404 if (FAILED(hrError))
405 {
406 ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError 0x%x: message too long, skipping\r\n", hrError);
407 }
408 else
409 {
410 ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError: message too long, skipping\r\n");
411 }
412 }
413
414 szMsg[countof(szMsg)-1] = '\0';
415 OutputDebugStringA(szMsg);
416 }
417}
418
419
420DAPIV_(void) Dutil_TraceErrorSource(
421 __in_z LPCSTR szFile,
422 __in int iLine,
423 __in REPORT_LEVEL rl,
424 __in UINT source,
425 __in HRESULT hr,
426 __in_z __format_string LPCSTR szFormat,
427 ...
428 )
429{
430 // if this is NOT an error report and we're not logging at this level, bail
431 if (REPORT_ERROR != rl && Dutil_rlCurrentTrace < rl)
432 {
433 return;
434 }
435
436 if (DUTIL_SOURCE_UNKNOWN != source && vpfnTraceErrorCallback)
437 {
438 va_list args;
439 va_start(args, szFormat);
440 vpfnTraceErrorCallback(szFile, iLine, rl, source, hr, szFormat, args);
441 va_end(args);
442 }
443}
444
445
446/*******************************************************************
447Dutil_RootFailure
448
449*******************************************************************/
450extern "C" void DAPI Dutil_RootFailure(
451 __in_z LPCSTR szFile,
452 __in int iLine,
453 __in HRESULT hrError
454 )
455{
456#ifndef DEBUG
457 UNREFERENCED_PARAMETER(szFile);
458 UNREFERENCED_PARAMETER(iLine);
459 UNREFERENCED_PARAMETER(hrError);
460#endif // DEBUG
461
462 TraceError(hrError, "Root failure at %s:%d", szFile, iLine);
463}
464
465/*******************************************************************
466 LoadSystemLibrary - Fully qualifies the path to a module in the
467 Windows system directory and loads it.
468
469 Returns
470 E_MODNOTFOUND - The module could not be found.
471 * - Another error occured.
472********************************************************************/
473extern "C" HRESULT DAPI LoadSystemLibrary(
474 __in_z LPCWSTR wzModuleName,
475 __out HMODULE *phModule
476 )
477{
478 HRESULT hr = LoadSystemLibraryWithPath(wzModuleName, phModule, NULL);
479 return hr;
480}
481
482/*******************************************************************
483 LoadSystemLibraryWithPath - Fully qualifies the path to a module in
484 the Windows system directory and loads it
485 and returns the path
486
487 Returns
488 E_MODNOTFOUND - The module could not be found.
489 * - Another error occured.
490********************************************************************/
491extern "C" HRESULT DAPI LoadSystemLibraryWithPath(
492 __in_z LPCWSTR wzModuleName,
493 __out HMODULE *phModule,
494 __deref_out_z_opt LPWSTR* psczPath
495 )
496{
497 HRESULT hr = S_OK;
498 DWORD cch = 0;
499 WCHAR wzPath[MAX_PATH] = { };
500
501 cch = ::GetSystemDirectoryW(wzPath, MAX_PATH);
502 DExitOnNullWithLastError(cch, hr, "Failed to get the Windows system directory.");
503
504 if (L'\\' != wzPath[cch - 1])
505 {
506 hr = ::StringCchCatNW(wzPath, MAX_PATH, L"\\", 1);
507 DExitOnRootFailure(hr, "Failed to terminate the string with a backslash.");
508 }
509
510 hr = ::StringCchCatW(wzPath, MAX_PATH, wzModuleName);
511 DExitOnRootFailure(hr, "Failed to create the fully-qualified path to %ls.", wzModuleName);
512
513 *phModule = ::LoadLibraryW(wzPath);
514 DExitOnNullWithLastError(*phModule, hr, "Failed to load the library %ls.", wzModuleName);
515
516 if (psczPath)
517 {
518 hr = StrAllocString(psczPath, wzPath, MAX_PATH);
519 DExitOnFailure(hr, "Failed to copy the path to library.");
520 }
521
522LExit:
523 return hr;
524}
diff --git a/src/libs/dutil/WixToolset.DUtil/dutil.nuspec b/src/libs/dutil/WixToolset.DUtil/dutil.nuspec
new file mode 100644
index 00000000..3499a2d5
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/dutil.nuspec
@@ -0,0 +1,27 @@
1<?xml version="1.0"?>
2<package >
3 <metadata>
4 <id>$id$</id>
5 <version>$version$</version>
6 <authors>$authors$</authors>
7 <owners>$authors$</owners>
8 <license type="expression">MS-RL</license>
9 <projectUrl>https://github.com/wixtoolset/dutil</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\x64\dutil.lib" target="build\native\v140\x64" />
19 <file src="..\..\build\$configuration$\v140\x86\dutil.lib" target="build\native\v140\x86" />
20 <file src="..\..\build\$configuration$\v141\x64\dutil.lib" target="build\native\v141\x64" />
21 <file src="..\..\build\$configuration$\v141\x86\dutil.lib" target="build\native\v141\x86" />
22 <file src="..\..\build\$configuration$\v141\ARM64\dutil.lib" target="build\native\v141\ARM64" />
23 <file src="..\..\build\$configuration$\v142\x64\dutil.lib" target="build\native\v142\x64" />
24 <file src="..\..\build\$configuration$\v142\x86\dutil.lib" target="build\native\v142\x86" />
25 <file src="..\..\build\$configuration$\v142\ARM64\dutil.lib" target="build\native\v142\ARM64" />
26 </files>
27</package>
diff --git a/src/libs/dutil/WixToolset.DUtil/dutil.vcxproj b/src/libs/dutil/WixToolset.DUtil/dutil.vcxproj
new file mode 100644
index 00000000..4e341438
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/dutil.vcxproj
@@ -0,0 +1,183 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3
4<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
5 <ItemGroup Label="ProjectConfigurations">
6 <ProjectConfiguration Include="Debug|ARM64">
7 <Configuration>Debug</Configuration>
8 <Platform>ARM64</Platform>
9 </ProjectConfiguration>
10 <ProjectConfiguration Include="Debug|Win32">
11 <Configuration>Debug</Configuration>
12 <Platform>Win32</Platform>
13 </ProjectConfiguration>
14 <ProjectConfiguration Include="Release|ARM64">
15 <Configuration>Release</Configuration>
16 <Platform>ARM64</Platform>
17 </ProjectConfiguration>
18 <ProjectConfiguration Include="Release|Win32">
19 <Configuration>Release</Configuration>
20 <Platform>Win32</Platform>
21 </ProjectConfiguration>
22 <ProjectConfiguration Include="Debug|x64">
23 <Configuration>Debug</Configuration>
24 <Platform>x64</Platform>
25 </ProjectConfiguration>
26 <ProjectConfiguration Include="Release|x64">
27 <Configuration>Release</Configuration>
28 <Platform>x64</Platform>
29 </ProjectConfiguration>
30 </ItemGroup>
31
32 <PropertyGroup Label="Globals">
33 <ProjectGuid>{1244E671-F108-4334-BA52-8A7517F26ECD}</ProjectGuid>
34 <ConfigurationType>StaticLibrary</ConfigurationType>
35 <TargetName>dutil</TargetName>
36 <MultiTargetLibrary>true</MultiTargetLibrary>
37 <PlatformToolset>v142</PlatformToolset>
38 <CharacterSet>MultiByte</CharacterSet>
39 <Description>WiX Toolset native library foundation</Description>
40 <PackageId>WixToolset.DUtil</PackageId>
41 </PropertyGroup>
42
43 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
44 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
45
46 <Import Project="..\NativeMultiTargeting.Build.props" />
47
48 <ItemGroup>
49 <ClCompile Include="acl2util.cpp" />
50 <ClCompile Include="aclutil.cpp" />
51 <ClCompile Include="apputil.cpp" />
52 <ClCompile Include="apuputil.cpp" />
53 <ClCompile Include="atomutil.cpp" />
54 <ClCompile Include="butil.cpp" />
55 <ClCompile Include="buffutil.cpp" />
56 <ClCompile Include="cabcutil.cpp" />
57 <ClCompile Include="cabutil.cpp" />
58 <ClCompile Include="certutil.cpp" />
59 <ClCompile Include="conutil.cpp" />
60 <ClCompile Include="cryputil.cpp" />
61 <ClCompile Include="deputil.cpp" />
62 <ClCompile Include="dictutil.cpp" />
63 <ClCompile Include="dirutil.cpp" />
64 <ClCompile Include="dlutil.cpp" />
65 <ClCompile Include="dpiutil.cpp" />
66 <ClCompile Include="dutil.cpp">
67 <PrecompiledHeader>Create</PrecompiledHeader>
68 <DisableSpecificWarnings>4091;4458</DisableSpecificWarnings>
69 </ClCompile>
70 <ClCompile Include="eseutil.cpp" />
71 <ClCompile Include="fileutil.cpp" />
72 <ClCompile Include="gdiputil.cpp" />
73 <ClCompile Include="guidutil.cpp" />
74 <ClCompile Include="iis7util.cpp" />
75 <ClCompile Include="inetutil.cpp" />
76 <ClCompile Include="iniutil.cpp" />
77 <ClCompile Include="jsonutil.cpp" />
78 <ClCompile Include="locutil.cpp" />
79 <ClCompile Include="logutil.cpp" />
80 <ClCompile Include="memutil.cpp" />
81 <ClCompile Include="metautil.cpp" />
82 <ClCompile Include="monutil.cpp" />
83 <ClCompile Include="osutil.cpp" />
84 <ClCompile Include="path2utl.cpp" />
85 <ClCompile Include="pathutil.cpp" />
86 <ClCompile Include="perfutil.cpp" />
87 <ClCompile Include="polcutil.cpp" />
88 <ClCompile Include="proc2utl.cpp" />
89 <ClCompile Include="proc3utl.cpp" />
90 <ClCompile Include="procutil.cpp" />
91 <ClCompile Include="regutil.cpp" />
92 <ClCompile Include="resrutil.cpp" />
93 <ClCompile Include="reswutil.cpp" />
94 <ClCompile Include="rexutil.cpp" />
95 <ClCompile Include="rmutil.cpp" />
96 <ClCompile Include="rssutil.cpp" />
97 <ClCompile Include="sceutil.cpp" Condition=" Exists('$(SqlCESdkIncludePath)') " />
98 <ClCompile Include="shelutil.cpp" />
99 <ClCompile Include="sqlutil.cpp" />
100 <ClCompile Include="srputil.cpp" />
101 <ClCompile Include="strutil.cpp" />
102 <ClCompile Include="svcutil.cpp" />
103 <ClCompile Include="thmutil.cpp" />
104 <ClCompile Include="timeutil.cpp" />
105 <ClCompile Include="uncutil.cpp" />
106 <ClCompile Include="uriutil.cpp" />
107 <ClCompile Include="userutil.cpp" />
108 <ClCompile Include="verutil.cpp" />
109 <ClCompile Include="wiutil.cpp" />
110 <ClCompile Include="wuautil.cpp" />
111 <ClCompile Include="xmlutil.cpp" />
112 </ItemGroup>
113
114 <ItemGroup>
115 <ClInclude Include="inc\aclutil.h" />
116 <ClInclude Include="inc\apputil.h" />
117 <ClInclude Include="inc\apuputil.h" />
118 <ClInclude Include="inc\atomutil.h" />
119 <ClInclude Include="inc\buffutil.h" />
120 <ClInclude Include="inc\butil.h" />
121 <ClInclude Include="inc\cabcutil.h" />
122 <ClInclude Include="inc\cabutil.h" />
123 <ClInclude Include="inc\certutil.h" />
124 <ClInclude Include="inc\conutil.h" />
125 <ClInclude Include="inc\cryputil.h" />
126 <ClInclude Include="inc\deputil.h" />
127 <ClInclude Include="inc\dictutil.h" />
128 <ClInclude Include="inc\dirutil.h" />
129 <ClInclude Include="inc\dlutil.h" />
130 <ClInclude Include="inc\dpiutil.h" />
131 <ClInclude Include="inc\dutil.h" />
132 <ClInclude Include="inc\dutilsources.h" />
133 <ClInclude Include="inc\eseutil.h" />
134 <ClInclude Include="inc\fileutil.h" />
135 <ClInclude Include="inc\gdiputil.h" />
136 <ClInclude Include="inc\guidutil.h" />
137 <ClInclude Include="inc\inetutil.h" />
138 <ClInclude Include="inc\iniutil.h" />
139 <ClInclude Include="inc\jsonutil.h" />
140 <ClInclude Include="inc\locutil.h" />
141 <ClInclude Include="inc\logutil.h" />
142 <ClInclude Include="inc\memutil.h" />
143 <ClInclude Include="inc\metautil.h" />
144 <ClInclude Include="inc\monutil.h" />
145 <ClInclude Include="inc\osutil.h" />
146 <ClInclude Include="inc\pathutil.h" />
147 <ClInclude Include="inc\perfutil.h" />
148 <ClInclude Include="inc\polcutil.h" />
149 <ClInclude Include="inc\procutil.h" />
150 <ClInclude Include="inc\regutil.h" />
151 <ClInclude Include="inc\resrutil.h" />
152 <ClInclude Include="inc\reswutil.h" />
153 <ClInclude Include="inc\rexutil.h" />
154 <ClInclude Include="inc\rssutil.h" />
155 <ClInclude Include="inc\sceutil.h" />
156 <ClInclude Include="inc\shelutil.h" />
157 <ClInclude Include="inc\sqlutil.h" />
158 <ClInclude Include="inc\srputil.h" />
159 <ClInclude Include="inc\strutil.h" />
160 <ClInclude Include="inc\svcutil.h" />
161 <ClInclude Include="inc\thmutil.h" />
162 <ClInclude Include="inc\timeutil.h" />
163 <ClInclude Include="inc\uriutil.h" />
164 <ClInclude Include="inc\userutil.h" />
165 <ClInclude Include="inc\verutil.h" />
166 <ClInclude Include="inc\wiutil.h" />
167 <ClInclude Include="inc\wuautil.h" />
168 <ClInclude Include="inc\xmlutil.h" />
169 <ClInclude Include="precomp.h" />
170 </ItemGroup>
171
172 <ItemGroup>
173 <None Include="packages.config" />
174 <None Include="xsd\thmutil.xsd" />
175 </ItemGroup>
176
177 <ItemGroup>
178 <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" />
179 <PackageReference Include="Nerdbank.GitVersioning" Version="3.3.37" />
180 </ItemGroup>
181
182 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
183</Project>
diff --git a/src/libs/dutil/WixToolset.DUtil/dutil.vcxproj.filters b/src/libs/dutil/WixToolset.DUtil/dutil.vcxproj.filters
new file mode 100644
index 00000000..b93d166b
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/dutil.vcxproj.filters
@@ -0,0 +1,372 @@
1<?xml version="1.0" encoding="utf-8"?>
2<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3 <ItemGroup>
4 <Filter Include="Source Files">
5 <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
6 <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
7 </Filter>
8 <Filter Include="Header Files">
9 <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
10 <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
11 </Filter>
12 <Filter Include="Resource Files">
13 <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
14 <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
15 </Filter>
16 </ItemGroup>
17 <ItemGroup>
18 <ClCompile Include="acl2util.cpp">
19 <Filter>Source Files</Filter>
20 </ClCompile>
21 <ClCompile Include="aclutil.cpp">
22 <Filter>Source Files</Filter>
23 </ClCompile>
24 <ClCompile Include="apputil.cpp">
25 <Filter>Source Files</Filter>
26 </ClCompile>
27 <ClCompile Include="apuputil.cpp">
28 <Filter>Source Files</Filter>
29 </ClCompile>
30 <ClCompile Include="atomutil.cpp">
31 <Filter>Source Files</Filter>
32 </ClCompile>
33 <ClCompile Include="buffutil.cpp">
34 <Filter>Source Files</Filter>
35 </ClCompile>
36 <ClCompile Include="butil.cpp">
37 <Filter>Source Files</Filter>
38 </ClCompile>
39 <ClCompile Include="cabcutil.cpp">
40 <Filter>Source Files</Filter>
41 </ClCompile>
42 <ClCompile Include="cabutil.cpp">
43 <Filter>Source Files</Filter>
44 </ClCompile>
45 <ClCompile Include="certutil.cpp">
46 <Filter>Source Files</Filter>
47 </ClCompile>
48 <ClCompile Include="conutil.cpp">
49 <Filter>Source Files</Filter>
50 </ClCompile>
51 <ClCompile Include="cryputil.cpp">
52 <Filter>Source Files</Filter>
53 </ClCompile>
54 <ClCompile Include="dictutil.cpp">
55 <Filter>Source Files</Filter>
56 </ClCompile>
57 <ClCompile Include="dirutil.cpp">
58 <Filter>Source Files</Filter>
59 </ClCompile>
60 <ClCompile Include="dlutil.cpp">
61 <Filter>Source Files</Filter>
62 </ClCompile>
63 <ClCompile Include="dpiutil.cpp">
64 <Filter>Source Files</Filter>
65 </ClCompile>
66 <ClCompile Include="dutil.cpp">
67 <Filter>Source Files</Filter>
68 </ClCompile>
69 <ClCompile Include="eseutil.cpp">
70 <Filter>Source Files</Filter>
71 </ClCompile>
72 <ClCompile Include="fileutil.cpp">
73 <Filter>Source Files</Filter>
74 </ClCompile>
75 <ClCompile Include="gdiputil.cpp">
76 <Filter>Source Files</Filter>
77 </ClCompile>
78 <ClCompile Include="guidutil.cpp">
79 <Filter>Source Files</Filter>
80 </ClCompile>
81 <ClCompile Include="iis7util.cpp">
82 <Filter>Source Files</Filter>
83 </ClCompile>
84 <ClCompile Include="inetutil.cpp">
85 <Filter>Source Files</Filter>
86 </ClCompile>
87 <ClCompile Include="jsonutil.cpp">
88 <Filter>Source Files</Filter>
89 </ClCompile>
90 <ClCompile Include="locutil.cpp">
91 <Filter>Source Files</Filter>
92 </ClCompile>
93 <ClCompile Include="logutil.cpp">
94 <Filter>Source Files</Filter>
95 </ClCompile>
96 <ClCompile Include="memutil.cpp">
97 <Filter>Source Files</Filter>
98 </ClCompile>
99 <ClCompile Include="metautil.cpp">
100 <Filter>Source Files</Filter>
101 </ClCompile>
102 <ClCompile Include="monutil.cpp">
103 <Filter>Source Files</Filter>
104 </ClCompile>
105 <ClCompile Include="osutil.cpp">
106 <Filter>Source Files</Filter>
107 </ClCompile>
108 <ClCompile Include="path2utl.cpp">
109 <Filter>Source Files</Filter>
110 </ClCompile>
111 <ClCompile Include="pathutil.cpp">
112 <Filter>Source Files</Filter>
113 </ClCompile>
114 <ClCompile Include="perfutil.cpp">
115 <Filter>Source Files</Filter>
116 </ClCompile>
117 <ClCompile Include="proc2utl.cpp">
118 <Filter>Source Files</Filter>
119 </ClCompile>
120 <ClCompile Include="procutil.cpp">
121 <Filter>Source Files</Filter>
122 </ClCompile>
123 <ClCompile Include="resrutil.cpp">
124 <Filter>Source Files</Filter>
125 </ClCompile>
126 <ClCompile Include="reswutil.cpp">
127 <Filter>Source Files</Filter>
128 </ClCompile>
129 <ClCompile Include="rexutil.cpp">
130 <Filter>Source Files</Filter>
131 </ClCompile>
132 <ClCompile Include="rmutil.cpp">
133 <Filter>Source Files</Filter>
134 </ClCompile>
135 <ClCompile Include="rssutil.cpp">
136 <Filter>Source Files</Filter>
137 </ClCompile>
138 <ClCompile Include="shelutil.cpp">
139 <Filter>Source Files</Filter>
140 </ClCompile>
141 <ClCompile Include="sqlutil.cpp">
142 <Filter>Source Files</Filter>
143 </ClCompile>
144 <ClCompile Include="strutil.cpp">
145 <Filter>Source Files</Filter>
146 </ClCompile>
147 <ClCompile Include="thmutil.cpp">
148 <Filter>Source Files</Filter>
149 </ClCompile>
150 <ClCompile Include="timeutil.cpp">
151 <Filter>Source Files</Filter>
152 </ClCompile>
153 <ClCompile Include="uncutil.cpp">
154 <Filter>Source Files</Filter>
155 </ClCompile>
156 <ClCompile Include="uriutil.cpp">
157 <Filter>Source Files</Filter>
158 </ClCompile>
159 <ClCompile Include="userutil.cpp">
160 <Filter>Source Files</Filter>
161 </ClCompile>
162 <ClCompile Include="verutil.cpp">
163 <Filter>Source Files</Filter>
164 </ClCompile>
165 <ClCompile Include="wiutil.cpp">
166 <Filter>Source Files</Filter>
167 </ClCompile>
168 <ClCompile Include="xmlutil.cpp">
169 <Filter>Source Files</Filter>
170 </ClCompile>
171 <ClCompile Include="svcutil.cpp">
172 <Filter>Source Files</Filter>
173 </ClCompile>
174 <ClCompile Include="regutil.cpp">
175 <Filter>Source Files</Filter>
176 </ClCompile>
177 <ClCompile Include="iniutil.cpp">
178 <Filter>Source Files</Filter>
179 </ClCompile>
180 <ClCompile Include="proc3utl.cpp">
181 <Filter>Source Files</Filter>
182 </ClCompile>
183 <ClCompile Include="wuautil.cpp">
184 <Filter>Source Files</Filter>
185 </ClCompile>
186 <ClCompile Include="srputil.cpp">
187 <Filter>Source Files</Filter>
188 </ClCompile>
189 <ClCompile Include="polcutil.cpp">
190 <Filter>Source Files</Filter>
191 </ClCompile>
192 <ClCompile Include="deputil.cpp">
193 <Filter>Source Files</Filter>
194 </ClCompile>
195 </ItemGroup>
196 <ItemGroup>
197 <ClInclude Include="inc\aclutil.h">
198 <Filter>Header Files</Filter>
199 </ClInclude>
200 <ClInclude Include="inc\apputil.h">
201 <Filter>Header Files</Filter>
202 </ClInclude>
203 <ClInclude Include="inc\apuputil.h">
204 <Filter>Header Files</Filter>
205 </ClInclude>
206 <ClInclude Include="inc\atomutil.h">
207 <Filter>Header Files</Filter>
208 </ClInclude>
209 <ClInclude Include="inc\buffutil.h">
210 <Filter>Header Files</Filter>
211 </ClInclude>
212 <ClInclude Include="inc\butil.h">
213 <Filter>Header Files</Filter>
214 </ClInclude>
215 <ClInclude Include="inc\cabcutil.h">
216 <Filter>Header Files</Filter>
217 </ClInclude>
218 <ClInclude Include="inc\cabutil.h">
219 <Filter>Header Files</Filter>
220 </ClInclude>
221 <ClInclude Include="inc\certutil.h">
222 <Filter>Header Files</Filter>
223 </ClInclude>
224 <ClInclude Include="inc\conutil.h">
225 <Filter>Header Files</Filter>
226 </ClInclude>
227 <ClInclude Include="inc\cryputil.h">
228 <Filter>Header Files</Filter>
229 </ClInclude>
230 <ClInclude Include="inc\dictutil.h">
231 <Filter>Header Files</Filter>
232 </ClInclude>
233 <ClInclude Include="inc\dirutil.h">
234 <Filter>Header Files</Filter>
235 </ClInclude>
236 <ClInclude Include="inc\dlutil.h">
237 <Filter>Header Files</Filter>
238 </ClInclude>
239 <ClInclude Include="inc\dpiutil.h">
240 <Filter>Header Files</Filter>
241 </ClInclude>
242 <ClInclude Include="inc\dutil.h">
243 <Filter>Header Files</Filter>
244 </ClInclude>
245 <ClInclude Include="inc\dutilsources.h">
246 <Filter>Header Files</Filter>
247 </ClInclude>
248 <ClInclude Include="inc\eseutil.h">
249 <Filter>Header Files</Filter>
250 </ClInclude>
251 <ClInclude Include="inc\fileutil.h">
252 <Filter>Header Files</Filter>
253 </ClInclude>
254 <ClInclude Include="inc\gdiputil.h">
255 <Filter>Header Files</Filter>
256 </ClInclude>
257 <ClInclude Include="inc\guidutil.h">
258 <Filter>Header Files</Filter>
259 </ClInclude>
260 <ClInclude Include="inc\inetutil.h">
261 <Filter>Header Files</Filter>
262 </ClInclude>
263 <ClInclude Include="inc\jsonutil.h">
264 <Filter>Header Files</Filter>
265 </ClInclude>
266 <ClInclude Include="inc\locutil.h">
267 <Filter>Header Files</Filter>
268 </ClInclude>
269 <ClInclude Include="inc\logutil.h">
270 <Filter>Header Files</Filter>
271 </ClInclude>
272 <ClInclude Include="inc\memutil.h">
273 <Filter>Header Files</Filter>
274 </ClInclude>
275 <ClInclude Include="inc\metautil.h">
276 <Filter>Header Files</Filter>
277 </ClInclude>
278 <ClInclude Include="inc\monutil.h">
279 <Filter>Header Files</Filter>
280 </ClInclude>
281 <ClInclude Include="inc\osutil.h">
282 <Filter>Header Files</Filter>
283 </ClInclude>
284 <ClInclude Include="inc\pathutil.h">
285 <Filter>Header Files</Filter>
286 </ClInclude>
287 <ClInclude Include="inc\perfutil.h">
288 <Filter>Header Files</Filter>
289 </ClInclude>
290 <ClInclude Include="inc\procutil.h">
291 <Filter>Header Files</Filter>
292 </ClInclude>
293 <ClInclude Include="inc\regutil.h">
294 <Filter>Header Files</Filter>
295 </ClInclude>
296 <ClInclude Include="inc\resrutil.h">
297 <Filter>Header Files</Filter>
298 </ClInclude>
299 <ClInclude Include="inc\reswutil.h">
300 <Filter>Header Files</Filter>
301 </ClInclude>
302 <ClInclude Include="inc\rmutil.h">
303 <Filter>Header Files</Filter>
304 </ClInclude>
305 <ClInclude Include="inc\rexutil.h">
306 <Filter>Header Files</Filter>
307 </ClInclude>
308 <ClInclude Include="inc\rssutil.h">
309 <Filter>Header Files</Filter>
310 </ClInclude>
311 <ClInclude Include="inc\sceutil.h">
312 <Filter>Header Files</Filter>
313 </ClInclude>
314 <ClInclude Include="inc\shelutil.h">
315 <Filter>Header Files</Filter>
316 </ClInclude>
317 <ClInclude Include="inc\sqlutil.h">
318 <Filter>Header Files</Filter>
319 </ClInclude>
320 <ClInclude Include="inc\strutil.h">
321 <Filter>Header Files</Filter>
322 </ClInclude>
323 <ClInclude Include="inc\thmutil.h">
324 <Filter>Header Files</Filter>
325 </ClInclude>
326 <ClInclude Include="inc\timeutil.h">
327 <Filter>Header Files</Filter>
328 </ClInclude>
329 <ClInclude Include="inc\uriutil.h">
330 <Filter>Header Files</Filter>
331 </ClInclude>
332 <ClInclude Include="inc\userutil.h">
333 <Filter>Header Files</Filter>
334 </ClInclude>
335 <ClInclude Include="inc\verutil.h">
336 <Filter>Header Files</Filter>
337 </ClInclude>
338 <ClInclude Include="inc\wiutil.h">
339 <Filter>Header Files</Filter>
340 </ClInclude>
341 <ClInclude Include="inc\xmlutil.h">
342 <Filter>Header Files</Filter>
343 </ClInclude>
344 <ClInclude Include="precomp.h">
345 <Filter>Header Files</Filter>
346 </ClInclude>
347 <ClInclude Include="inc\svcutil.h">
348 <Filter>Header Files</Filter>
349 </ClInclude>
350 <ClInclude Include="inc\iniutil.h">
351 <Filter>Header Files</Filter>
352 </ClInclude>
353 <ClInclude Include="inc\wuautil.h">
354 <Filter>Header Files</Filter>
355 </ClInclude>
356 <ClInclude Include="inc\srputil.h">
357 <Filter>Header Files</Filter>
358 </ClInclude>
359 <ClInclude Include="inc\polcutil.h">
360 <Filter>Header Files</Filter>
361 </ClInclude>
362 <ClInclude Include="inc\deputil.h">
363 <Filter>Header Files</Filter>
364 </ClInclude>
365 </ItemGroup>
366 <ItemGroup>
367 <None Include="xsd\thmutil.xsd">
368 <Filter>Header Files</Filter>
369 </None>
370 <None Include="packages.config" />
371 </ItemGroup>
372</Project> \ No newline at end of file
diff --git a/src/libs/dutil/WixToolset.DUtil/eseutil.cpp b/src/libs/dutil/WixToolset.DUtil/eseutil.cpp
new file mode 100644
index 00000000..b9455d4b
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/eseutil.cpp
@@ -0,0 +1,1340 @@
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// Exit macros
7#define EseExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_ESEUTIL, x, s, __VA_ARGS__)
8#define EseExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_ESEUTIL, x, s, __VA_ARGS__)
9#define EseExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_ESEUTIL, x, s, __VA_ARGS__)
10#define EseExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_ESEUTIL, x, s, __VA_ARGS__)
11#define EseExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_ESEUTIL, x, s, __VA_ARGS__)
12#define EseExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_ESEUTIL, x, s, __VA_ARGS__)
13#define EseExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_ESEUTIL, p, x, e, s, __VA_ARGS__)
14#define EseExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_ESEUTIL, p, x, s, __VA_ARGS__)
15#define EseExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_ESEUTIL, p, x, e, s, __VA_ARGS__)
16#define EseExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_ESEUTIL, p, x, s, __VA_ARGS__)
17#define EseExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_ESEUTIL, e, x, s, __VA_ARGS__)
18#define EseExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_ESEUTIL, g, x, s, __VA_ARGS__)
19
20struct ESE_QUERY
21{
22 ESE_QUERY_TYPE qtQueryType;
23 BOOL fIndexRangeSet;
24
25 JET_SESID jsSession;
26 JET_TABLEID jtTable;
27
28 DWORD dwColumns;
29 void *pvData[10]; // The data queried for for this column
30 DWORD cbData[10]; // One for each column, describes the size of the corresponding entry in ppvData
31};
32
33// Todo: convert more JET_ERR to HRESULTS here
34HRESULT HresultFromJetError(JET_ERR jEr)
35{
36 HRESULT hr = S_OK;
37
38 switch (jEr)
39 {
40 case JET_errSuccess:
41 return S_OK;
42
43 case JET_wrnNyi:
44 return E_NOTIMPL;
45 break;
46
47 case JET_errOutOfMemory:
48 hr = E_OUTOFMEMORY;
49 break;
50
51 case JET_errInvalidParameter: __fallthrough;
52 case JET_errInvalidInstance:
53 hr = E_INVALIDARG;
54 break;
55
56 case JET_errDatabaseInUse:
57 hr = HRESULT_FROM_WIN32(ERROR_DEVICE_IN_USE);
58 break;
59
60 case JET_errKeyDuplicate:
61 hr = HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS);
62 break;
63
64 case JET_errInvalidSystemPath: __fallthrough;
65 case JET_errInvalidLogDirectory: __fallthrough;
66 case JET_errInvalidPath: __fallthrough;
67 case JET_errDatabaseInvalidPath:
68 hr = HRESULT_FROM_WIN32(ERROR_INVALID_NAME);
69 break;
70
71 case JET_errDatabaseLocked:
72 hr = HRESULT_FROM_WIN32(ERROR_FILE_CHECKED_OUT);
73 break;
74
75 case JET_errInvalidDatabase:
76 hr = HRESULT_FROM_WIN32(ERROR_INTERNAL_DB_CORRUPTION);
77 break;
78
79 case JET_errCallbackNotResolved:
80 hr = HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION);
81 break;
82
83 case JET_errNoCurrentRecord: __fallthrough;
84 case JET_errRecordNotFound: __fallthrough;
85 case JET_errFileNotFound: __fallthrough;
86 case JET_errObjectNotFound:
87 hr = E_NOTFOUND;
88 break;
89
90 case JET_wrnBufferTruncated:
91 hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
92 break;
93
94 case JET_errFileAccessDenied:
95 hr = E_ACCESSDENIED;
96 break;
97
98 default:
99 hr = E_FAIL;
100 }
101
102 // Log the actual Jet error code so we have record of it before it's morphed into an HRESULT to be compatible with the rest of our code
103 ExitTraceSource(DUTIL_SOURCE_ESEUTIL, hr, "Encountered Jet Error: 0x%08x", jEr);
104
105 return hr;
106}
107
108#define ExitOnJetFailure(e, x, s, ...) { x = HresultFromJetError(e); if (S_OK != x) { ExitTraceSource(DUTIL_SOURCE_ESEUTIL, x, s, __VA_ARGS__); goto LExit; }}
109#define ExitOnRootJetFailure(e, x, s, ...) { x = HresultFromJetError(e); if (S_OK != x) { Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceSource(DUTIL_SOURCE_ESEUTIL, x, s, __VA_ARGS__); goto LExit; }}
110
111HRESULT DAPI EseBeginSession(
112 __out JET_INSTANCE *pjiInstance,
113 __out JET_SESID *pjsSession,
114 __in_z LPCWSTR pszInstance,
115 __in_z LPCWSTR pszPath
116 )
117{
118 HRESULT hr = S_OK;
119 JET_ERR jEr = JET_errSuccess;
120 LPSTR pszAnsiInstance = NULL;
121 LPSTR pszAnsiPath = NULL;
122
123 hr = DirEnsureExists(pszPath, NULL);
124 EseExitOnFailure(hr, "Failed to ensure database directory exists");
125
126 // Sigh. JETblue requires Vista and up for the wide character version of this function, so we'll convert to ANSI before calling,
127 // likely breaking everyone with unicode characters in their path.
128 hr = StrAnsiAllocString(&pszAnsiInstance, pszInstance, 0, CP_ACP);
129 EseExitOnFailure(hr, "Failed converting instance name to ansi");
130
131 hr = StrAnsiAllocString(&pszAnsiPath, pszPath, 0, CP_ACP);
132 EseExitOnFailure(hr, "Failed converting session path name to ansi");
133
134 jEr = JetCreateInstanceA(pjiInstance, pszAnsiInstance);
135 ExitOnJetFailure(jEr, hr, "Failed to create instance");
136
137 jEr = JetSetSystemParameter(pjiInstance, NULL, JET_paramSystemPath, NULL, pszAnsiPath);
138 ExitOnJetFailure(jEr, hr, "Failed to set jet system path to: %s", pszAnsiPath);
139
140 // This makes sure log files that are created are created next to the database, not next to our EXE (note they last after execution)
141 jEr = JetSetSystemParameter(pjiInstance, NULL, JET_paramLogFilePath, NULL, pszAnsiPath);
142 ExitOnJetFailure(jEr, hr, "Failed to set jet log file path to: %s", pszAnsiPath);
143
144 jEr = JetSetSystemParameter(pjiInstance, NULL, JET_paramMaxOpenTables, 10, NULL);
145 ExitOnJetFailure(jEr, hr, "Failed to set jet max open tables parameter");
146
147 // TODO: Use callback hooks so that Jet Engine uses our memory allocation methods, etc.? (search docs for "JET_PFNREALLOC" - there are other callbacks too)
148
149 jEr = JetInit(pjiInstance);
150 ExitOnJetFailure(jEr, hr, "Failed to initialize jet engine instance");
151
152 jEr = JetBeginSession(*pjiInstance, pjsSession, NULL, NULL);
153 ExitOnJetFailure(jEr, hr, "Failed to begin jet session");
154
155LExit:
156 ReleaseStr(pszAnsiInstance);
157 ReleaseStr(pszAnsiPath);
158
159 return hr;
160}
161
162HRESULT DAPI EseEndSession(
163 __in JET_INSTANCE jiInstance,
164 __in JET_SESID jsSession
165 )
166{
167 HRESULT hr = S_OK;
168 JET_ERR jEr = JET_errSuccess;
169
170 jEr = JetEndSession(jsSession, 0);
171 ExitOnJetFailure(jEr, hr, "Failed to end jet session");
172
173 jEr = JetTerm(jiInstance);
174 ExitOnJetFailure(jEr, hr, "Failed to uninitialize jet engine instance");
175
176LExit:
177 return hr;
178}
179
180// Utility function used by EnsureSchema()
181HRESULT AllocColumnCreateStruct(
182 __in const ESE_TABLE_SCHEMA *ptsSchema,
183 __deref_out JET_COLUMNCREATE **ppjccColumnCreate
184 )
185{
186 HRESULT hr = S_OK;
187 DWORD_PTR i;
188 size_t cbAllocSize = 0;
189
190 hr = ::SizeTMult(ptsSchema->dwColumns, sizeof(JET_COLUMNCREATE), &(cbAllocSize));
191 EseExitOnFailure(hr, "Maximum allocation exceeded.");
192
193 *ppjccColumnCreate = static_cast<JET_COLUMNCREATE*>(MemAlloc(cbAllocSize, TRUE));
194 EseExitOnNull(*ppjccColumnCreate, hr, E_OUTOFMEMORY, "Failed to allocate column create structure for database");
195
196 for (i = 0; i < ptsSchema->dwColumns; ++i)
197 {
198 (*ppjccColumnCreate)[i].cbStruct = sizeof(JET_COLUMNCREATE);
199
200 hr = StrAnsiAllocString(&(*ppjccColumnCreate)[i].szColumnName, ptsSchema->pcsColumns[i].pszName, 0, CP_ACP);
201 EseExitOnFailure(hr, "Failed to allocate ansi column name: %ls", ptsSchema->pcsColumns[i].pszName);
202
203 (*ppjccColumnCreate)[i].coltyp = ptsSchema->pcsColumns[i].jcColumnType;
204
205 if (JET_coltypText == (*ppjccColumnCreate)[i].coltyp)
206 {
207 (*ppjccColumnCreate)[i].cbMax = 256;
208 }
209 else if (JET_coltypLongText == (*ppjccColumnCreate)[i].coltyp)
210 {
211 (*ppjccColumnCreate)[i].cbMax = 2147483648;
212 (*ppjccColumnCreate)[i].grbit = JET_bitColumnTagged; // LongText columns must be tagged
213 ptsSchema->pcsColumns[i].fNullable = TRUE;
214 }
215 else if (JET_coltypLong == (*ppjccColumnCreate)[i].coltyp)
216 {
217 (*ppjccColumnCreate)[i].cbMax = 4;
218
219 if (ptsSchema->pcsColumns[i].fAutoIncrement)
220 {
221 (*ppjccColumnCreate)[i].grbit |= JET_bitColumnAutoincrement;
222 }
223 }
224
225 if (!(ptsSchema->pcsColumns[i].fNullable))
226 {
227 (*ppjccColumnCreate)[i].grbit |= JET_bitColumnNotNULL;
228 }
229
230 (*ppjccColumnCreate)[i].pvDefault = NULL;
231 (*ppjccColumnCreate)[i].cbDefault = 0;
232 (*ppjccColumnCreate)[i].cp = 1200;
233 (*ppjccColumnCreate)[i].columnid = 0;
234 (*ppjccColumnCreate)[i].err = 0;
235 }
236
237LExit:
238 return hr;
239}
240
241HRESULT FreeColumnCreateStruct(
242 __in_ecount(dwColumns) JET_COLUMNCREATE *pjccColumnCreate,
243 __in DWORD dwColumns
244 )
245{
246 HRESULT hr = S_OK;
247 DWORD i;
248
249 for (i = 0; i < dwColumns; ++i)
250 {
251 ReleaseStr((pjccColumnCreate[i]).szColumnName);
252 }
253
254 hr = MemFree(pjccColumnCreate);
255 EseExitOnFailure(hr, "Failed to release core column create struct");
256
257LExit:
258 return hr;
259}
260
261// Utility function used by EnsureSchema()
262HRESULT AllocIndexCreateStruct(
263 __in const ESE_TABLE_SCHEMA *ptsSchema,
264 __deref_out JET_INDEXCREATE **ppjicIndexCreate
265 )
266{
267 HRESULT hr = S_OK;
268 LPSTR pszMultiSzKeys = NULL;
269 LPSTR pszIndexName = NULL;
270 LPSTR pszTempString = NULL;
271 BOOL fKeyColumns = FALSE;
272 DWORD_PTR i;
273
274 for (i=0; i < ptsSchema->dwColumns; ++i)
275 {
276 if (ptsSchema->pcsColumns[i].fKey)
277 {
278 hr = StrAnsiAllocString(&pszTempString, ptsSchema->pcsColumns[i].pszName, 0, CP_ACP);
279 EseExitOnFailure(hr, "Failed to convert string to ansi: %ls", ptsSchema->pcsColumns[i].pszName);
280
281 hr = StrAnsiAllocConcat(&pszMultiSzKeys, "+", 0);
282 EseExitOnFailure(hr, "Failed to append plus sign to multisz string: %s", pszTempString);
283
284 hr = StrAnsiAllocConcat(&pszMultiSzKeys, pszTempString, 0);
285 EseExitOnFailure(hr, "Failed to append column name to multisz string: %s", pszTempString);
286
287 ReleaseNullStr(pszTempString);
288
289 // All question marks will be converted to null characters later; this is just to trick dutil
290 // into letting us create an ansi, double-null-terminated list of single-null-terminated strings
291 hr = StrAnsiAllocConcat(&pszMultiSzKeys, "?", 0);
292 EseExitOnFailure(hr, "Failed to append placeholder character to multisz string: %hs", pszMultiSzKeys);
293
294 // Record that at least one key column was found
295 fKeyColumns = TRUE;
296 }
297 }
298
299 // If no key columns were found, don't create an index - just return
300 if (!fKeyColumns)
301 {
302 ExitFunction1(hr = S_OK);
303 }
304
305 hr = StrAnsiAllocString(&pszIndexName, ptsSchema->pszName, 0, CP_ACP);
306 EseExitOnFailure(hr, "Failed to allocate ansi string version of %ls", ptsSchema->pszName);
307
308 hr = StrAnsiAllocConcat(&pszIndexName, "_Index", 0);
309 EseExitOnFailure(hr, "Failed to append table name string version of %ls", ptsSchema->pszName);
310
311 *ppjicIndexCreate = static_cast<JET_INDEXCREATE*>(MemAlloc(sizeof(JET_INDEXCREATE), TRUE));
312 EseExitOnNull(*ppjicIndexCreate, hr, E_OUTOFMEMORY, "Failed to allocate index create structure for database");
313
314 // Record the size including both null terminators - the struct requires this
315 size_t cchSize = 0;
316 hr = ::StringCchLengthA(pszMultiSzKeys, STRSAFE_MAX_LENGTH, &cchSize);
317 EseExitOnRootFailure(hr, "Failed to get size of keys string");
318
319 ++cchSize; // add 1 to include null character at the end
320
321 // At this point convert all question marks to null characters
322 for (i = 0; i < cchSize; ++i)
323 {
324 if ('?' == pszMultiSzKeys[i])
325 {
326 pszMultiSzKeys[i] = '\0';
327 }
328 }
329
330 (*ppjicIndexCreate)->cbStruct = sizeof(JET_INDEXCREATE);
331 (*ppjicIndexCreate)->szIndexName = pszIndexName;
332 (*ppjicIndexCreate)->szKey = pszMultiSzKeys;
333 (*ppjicIndexCreate)->cbKey = (DWORD)cchSize;
334 (*ppjicIndexCreate)->grbit = JET_bitIndexUnique | JET_bitIndexPrimary;
335 (*ppjicIndexCreate)->ulDensity = 80;
336 (*ppjicIndexCreate)->lcid = 1033;
337 (*ppjicIndexCreate)->pidxunicode = NULL;
338 (*ppjicIndexCreate)->cbVarSegMac = 0;
339 (*ppjicIndexCreate)->rgconditionalcolumn = NULL;
340 (*ppjicIndexCreate)->cConditionalColumn = 0;
341 (*ppjicIndexCreate)->err = 0;
342
343LExit:
344 ReleaseStr(pszTempString);
345
346 return hr;
347}
348
349HRESULT EnsureSchema(
350 __in JET_DBID jdbDb,
351 __in JET_SESID jsSession,
352 __in ESE_DATABASE_SCHEMA *pdsSchema
353 )
354{
355 HRESULT hr = S_OK;
356 JET_ERR jEr = JET_errSuccess;
357 BOOL fTransaction = FALSE;
358 DWORD dwTable;
359 DWORD dwColumn;
360 JET_TABLECREATE jtTableCreate = { };
361
362 // Set parameters which apply to all tables here
363 jtTableCreate.cbStruct = sizeof(jtTableCreate);
364 jtTableCreate.ulPages = 100;
365 jtTableCreate.ulDensity = 0; // per the docs, 0 means "use the default value"
366 jtTableCreate.cIndexes = 1;
367
368 hr = EseBeginTransaction(jsSession);
369 EseExitOnFailure(hr, "Failed to begin transaction to create tables");
370 fTransaction = TRUE;
371
372 for (dwTable = 0;dwTable < pdsSchema->dwTables; ++dwTable)
373 {
374 // Don't free this pointer - it's just a shortcut to the current table's name within the struct
375 LPCWSTR pwzTableName = pdsSchema->ptsTables[dwTable].pszName;
376
377 // Ensure table exists
378 hr = EseOpenTable(jsSession, jdbDb, pwzTableName, &pdsSchema->ptsTables[dwTable].jtTable);
379 if (E_NOTFOUND == hr) // if the table is missing, create it
380 {
381 // Fill out the JET_TABLECREATE struct
382 hr = StrAnsiAllocString(&jtTableCreate.szTableName, pdsSchema->ptsTables[dwTable].pszName, 0, CP_ACP);
383 EseExitOnFailure(hr, "Failed converting table name to ansi");
384
385 hr = AllocColumnCreateStruct(&(pdsSchema->ptsTables[dwTable]), &jtTableCreate.rgcolumncreate);
386 EseExitOnFailure(hr, "Failed to allocate column create struct");
387
388 hr = AllocIndexCreateStruct(&(pdsSchema->ptsTables[dwTable]), &jtTableCreate.rgindexcreate);
389 EseExitOnFailure(hr, "Failed to allocate index create struct");
390
391 jtTableCreate.cColumns = pdsSchema->ptsTables[dwTable].dwColumns;
392 jtTableCreate.tableid = NULL;
393
394 // TODO: Investigate why we can't create a table without a key column?
395 // Actually create the table using our JET_TABLECREATE struct
396 jEr = JetCreateTableColumnIndex(jsSession, jdbDb, &jtTableCreate);
397 ExitOnJetFailure(jEr, hr, "Failed to create %ls table", pwzTableName);
398
399 // Record the table ID in our cache
400 pdsSchema->ptsTables[dwTable].jtTable = jtTableCreate.tableid;
401
402 // Record the column IDs in our cache
403 for (dwColumn = 0; dwColumn < pdsSchema->ptsTables[dwTable].dwColumns; ++dwColumn)
404 {
405 pdsSchema->ptsTables[dwTable].pcsColumns[dwColumn].jcColumn = jtTableCreate.rgcolumncreate[dwColumn].columnid;
406 }
407
408 // Free and NULL things we allocated in this struct
409 ReleaseNullStr(jtTableCreate.szTableName);
410
411 hr = FreeColumnCreateStruct(jtTableCreate.rgcolumncreate, jtTableCreate.cColumns);
412 EseExitOnFailure(hr, "Failed to free column create struct");
413 jtTableCreate.rgcolumncreate = NULL;
414 }
415 else
416 {
417 // If the table already exists, grab the column ids and put them into our cache
418 for (dwColumn = 0;dwColumn < pdsSchema->ptsTables[dwTable].dwColumns; ++dwColumn)
419 {
420 // Don't free this - it's just a shortcut to the current column within the struct
421 ESE_COLUMN_SCHEMA *pcsColumn = &(pdsSchema->ptsTables[dwTable].pcsColumns[dwColumn]);
422 ULONG ulColumnSize = 0;
423 BOOL fNullable = pcsColumn->fNullable;
424
425 // Todo: this code is nearly duplicated from AllocColumnCreateStruct - factor it out!
426 if (JET_coltypText == pcsColumn->jcColumnType)
427 {
428 ulColumnSize = 256;
429 }
430 else if (JET_coltypLongText == pcsColumn->jcColumnType)
431 {
432 ulColumnSize = 2147483648;
433 fNullable = TRUE;
434 }
435 else if (JET_coltypLong == pcsColumn->jcColumnType)
436 {
437 ulColumnSize = 4;
438 fNullable = TRUE;
439 }
440
441 hr = EseEnsureColumn(jsSession, pdsSchema->ptsTables[dwTable].jtTable, pcsColumn->pszName, pcsColumn->jcColumnType, ulColumnSize, pcsColumn->fFixed, fNullable, &pcsColumn->jcColumn);
442 EseExitOnFailure(hr, "Failed to create column %u of %ls table", dwColumn, pwzTableName);
443 }
444 }
445 }
446
447LExit:
448 ReleaseStr(jtTableCreate.szTableName);
449
450 if (NULL != jtTableCreate.rgcolumncreate)
451 {
452 // Don't record the HRESULT here or it will override the return value of this function
453 FreeColumnCreateStruct(jtTableCreate.rgcolumncreate, jtTableCreate.cColumns);
454 }
455
456 if (fTransaction)
457 {
458 EseCommitTransaction(jsSession);
459 }
460
461 return hr;
462}
463
464// Todo: support overwrite flag? Unfortunately, requires WinXP and up
465// Todo: Add version parameter, and a built-in dutil table that stores the version of the database schema on disk - then allow overriding the "migrate to new schema" functionality with a callback
466HRESULT DAPI EseEnsureDatabase(
467 __in JET_SESID jsSession,
468 __in_z LPCWSTR pszFile,
469 __in ESE_DATABASE_SCHEMA *pdsSchema,
470 __out JET_DBID* pjdbDb,
471 __in BOOL fExclusive,
472 __in BOOL fReadonly
473 )
474{
475 HRESULT hr = S_OK;
476 JET_ERR jEr = JET_errSuccess;
477 JET_GRBIT jgrOptions = 0;
478 LPWSTR pszDir = NULL;
479 LPSTR pszAnsiFile = NULL;
480
481 // Sigh. JETblue requires Vista and up for the wide character version of this function, so we'll convert to ANSI before calling,
482 // likely breaking all those with unicode characters in their path.
483 hr = StrAnsiAllocString(&pszAnsiFile, pszFile, 0, CP_ACP);
484 EseExitOnFailure(hr, "Failed converting database name to ansi");
485
486 hr = PathGetDirectory(pszFile, &pszDir);
487 EseExitOnFailure(hr, "Failed to get directory that will contain database file");
488
489 hr = DirEnsureExists(pszDir, NULL);
490 EseExitOnFailure(hr, "Failed to ensure directory exists for database: %ls", pszDir);
491
492 if (FileExistsEx(pszFile, NULL))
493 {
494 if (fReadonly)
495 {
496 jgrOptions = jgrOptions | JET_bitDbReadOnly;
497 }
498
499 jEr = JetAttachDatabaseA(jsSession, pszAnsiFile, jgrOptions);
500 ExitOnJetFailure(jEr, hr, "Failed to attach to database %s", pszAnsiFile);
501
502 // This flag doesn't apply to attach, only applies to Open, so only set it after the attach
503 if (fExclusive)
504 {
505 jgrOptions = jgrOptions | JET_bitDbExclusive;
506 }
507
508 jEr = JetOpenDatabaseA(jsSession, pszAnsiFile, NULL, pjdbDb, jgrOptions);
509 ExitOnJetFailure(jEr, hr, "Failed to open database %s", pszAnsiFile);
510 }
511 else
512 {
513 jEr = JetCreateDatabase2A(jsSession, pszAnsiFile, 0, pjdbDb, 0);
514 ExitOnJetFailure(jEr, hr, "Failed to create database %ls", pszFile);
515 }
516
517 hr = EnsureSchema(*pjdbDb, jsSession, pdsSchema);
518 EseExitOnFailure(hr, "Failed to ensure database schema matches expectations");
519
520LExit:
521 ReleaseStr(pszDir);
522 ReleaseStr(pszAnsiFile);
523
524 return hr;
525}
526
527HRESULT DAPI EseCloseDatabase(
528 __in JET_SESID jsSession,
529 __in JET_DBID jdbDb
530 )
531{
532 HRESULT hr = S_OK;
533 JET_ERR jEr = JET_errSuccess;
534 JET_GRBIT jgrOptions = 0;
535
536 jEr = JetCloseDatabase(jsSession, jdbDb, jgrOptions);
537 ExitOnJetFailure(jEr, hr, "Failed to close database");
538
539LExit:
540 return hr;
541}
542
543HRESULT DAPI EseCreateTable(
544 __in JET_SESID jsSession,
545 __in JET_DBID jdbDb,
546 __in_z LPCWSTR pszTable,
547 __out JET_TABLEID *pjtTable
548 )
549{
550 HRESULT hr = S_OK;
551 JET_ERR jEr = JET_errSuccess;
552 LPSTR pszAnsiTable = NULL;
553
554 hr = StrAnsiAllocString(&pszAnsiTable, pszTable, 0, CP_ACP);
555 EseExitOnFailure(hr, "Failed converting table name to ansi");
556
557 jEr = JetCreateTableA(jsSession, jdbDb, pszAnsiTable, 100, 0, pjtTable);
558 ExitOnJetFailure(jEr, hr, "Failed to create table %s", pszAnsiTable);
559
560LExit:
561 ReleaseStr(pszAnsiTable);
562
563 return hr;
564}
565
566HRESULT DAPI EseOpenTable(
567 __in JET_SESID jsSession,
568 __in JET_DBID jdbDb,
569 __in_z LPCWSTR pszTable,
570 __out JET_TABLEID *pjtTable
571 )
572{
573 HRESULT hr = S_OK;
574 JET_ERR jEr = JET_errSuccess;
575 LPSTR pszAnsiTable = NULL;
576
577 hr = StrAnsiAllocString(&pszAnsiTable, pszTable, 0, CP_ACP);
578 EseExitOnFailure(hr, "Failed converting table name to ansi");
579
580 jEr = JetOpenTableA(jsSession, jdbDb, pszAnsiTable, NULL, 0, 0, pjtTable);
581 ExitOnJetFailure(jEr, hr, "Failed to open table %s", pszAnsiTable);
582
583LExit:
584 ReleaseStr(pszAnsiTable);
585
586 return hr;
587}
588
589HRESULT DAPI EseCloseTable(
590 __in JET_SESID jsSession,
591 __in JET_TABLEID jtTable
592 )
593{
594 HRESULT hr = S_OK;
595 JET_ERR jEr = JET_errSuccess;
596
597 jEr = JetCloseTable(jsSession, jtTable);
598 ExitOnJetFailure(jEr, hr, "Failed to close table");
599
600LExit:
601 return hr;
602}
603
604HRESULT DAPI EseEnsureColumn(
605 __in JET_SESID jsSession,
606 __in JET_TABLEID jtTable,
607 __in_z LPCWSTR pszColumnName,
608 __in JET_COLTYP jcColumnType,
609 __in ULONG ulColumnSize,
610 __in BOOL fFixed,
611 __in BOOL fNullable,
612 __out_opt JET_COLUMNID *pjcColumn
613 )
614{
615 HRESULT hr = S_OK;
616 JET_ERR jEr = JET_errSuccess;
617 LPSTR pszAnsiColumnName = NULL;
618 JET_COLUMNDEF jcdColumnDef = { sizeof(JET_COLUMNDEF) };
619 JET_COLUMNBASE jcdTempBase = { sizeof(JET_COLUMNBASE) };
620
621 hr = StrAnsiAllocString(&pszAnsiColumnName, pszColumnName, 0, CP_ACP);
622 EseExitOnFailure(hr, "Failed converting column name to ansi");
623
624 jEr = JetGetTableColumnInfoA(jsSession, jtTable, pszAnsiColumnName, &jcdTempBase, sizeof(JET_COLUMNBASE), JET_ColInfoBase);
625 if (JET_errSuccess == jEr)
626 {
627 // Return the found columnID
628 if (NULL != pjcColumn)
629 {
630 *pjcColumn = jcdTempBase.columnid;
631 }
632
633 ExitFunction1(hr = S_OK);
634 }
635 else if (JET_errColumnNotFound == jEr)
636 {
637 jEr = JET_errSuccess;
638 }
639 ExitOnJetFailure(jEr, hr, "Failed to check if column exists: %s", pszAnsiColumnName);
640
641 jcdColumnDef.columnid = 0;
642 jcdColumnDef.coltyp = jcColumnType;
643 jcdColumnDef.wCountry = 0;
644 jcdColumnDef.langid = 0;
645 jcdColumnDef.cp = 1200;
646 jcdColumnDef.wCollate = 0;
647 jcdColumnDef.cbMax = ulColumnSize;
648 jcdColumnDef.grbit = 0;
649
650 if (fFixed)
651 {
652 jcdColumnDef.grbit = jcdColumnDef.grbit | JET_bitColumnFixed;
653 }
654 if (!fNullable)
655 {
656 jcdColumnDef.grbit = jcdColumnDef.grbit | JET_bitColumnNotNULL;
657 }
658
659 jEr = JetAddColumnA(jsSession, jtTable, pszAnsiColumnName, &jcdColumnDef, NULL, 0, pjcColumn);
660 ExitOnJetFailure(jEr, hr, "Failed to add column %ls", pszColumnName);
661
662LExit:
663 ReleaseStr(pszAnsiColumnName);
664
665 return hr;
666}
667
668HRESULT DAPI EseGetColumn(
669 __in JET_SESID jsSession,
670 __in JET_TABLEID jtTable,
671 __in_z LPCWSTR pszColumnName,
672 __out JET_COLUMNID *pjcColumn
673 )
674{
675 HRESULT hr = S_OK;
676 JET_ERR jEr = JET_errSuccess;
677 LPSTR pszAnsiColumnName = NULL;
678 JET_COLUMNBASE jcdTempBase = { sizeof(JET_COLUMNBASE) };
679
680 hr = StrAnsiAllocString(&pszAnsiColumnName, pszColumnName, 0, CP_ACP);
681 EseExitOnFailure(hr, "Failed converting column name to ansi");
682
683 jEr = JetGetTableColumnInfoA(jsSession, jtTable, pszAnsiColumnName, &jcdTempBase, sizeof(JET_COLUMNBASE), JET_ColInfoBase);
684 if (JET_errSuccess == jEr)
685 {
686 // Return the found columnID
687 if (NULL != pjcColumn)
688 {
689 *pjcColumn = jcdTempBase.columnid;
690 }
691
692 ExitFunction1(hr = S_OK);
693 }
694 ExitOnJetFailure(jEr, hr, "Failed to check if column exists: %s", pszAnsiColumnName);
695
696LExit:
697 ReleaseStr(pszAnsiColumnName);
698
699 return hr;
700}
701
702HRESULT DAPI EseMoveCursor(
703 __in JET_SESID jsSession,
704 __in JET_TABLEID jtTable,
705 __in LONG lRow
706 )
707{
708 HRESULT hr = S_OK;
709 JET_ERR jEr = JET_errSuccess;
710
711 jEr = JetMove(jsSession, jtTable, lRow, 0);
712 ExitOnJetFailure(jEr, hr, "Failed to move jet cursor by amount: %d", lRow);
713
714LExit:
715 return hr;
716}
717
718HRESULT DAPI EseDeleteRow(
719 __in JET_SESID jsSession,
720 __in JET_TABLEID jtTable
721 )
722{
723 HRESULT hr = S_OK;
724 JET_ERR jEr = JET_errSuccess;
725
726 jEr = JetDelete(jsSession, jtTable);
727 ExitOnJetFailure(jEr, hr, "Failed to delete row");
728
729LExit:
730 return hr;
731}
732
733HRESULT DAPI EseBeginTransaction(
734 __in JET_SESID jsSession
735 )
736{
737 HRESULT hr = S_OK;
738 JET_ERR jEr = JET_errSuccess;
739
740 jEr = JetBeginTransaction(jsSession);
741 ExitOnJetFailure(jEr, hr, "Failed to begin transaction");
742
743LExit:
744 return hr;
745}
746
747HRESULT DAPI EseRollbackTransaction(
748 __in JET_SESID jsSession,
749 __in BOOL fAll
750 )
751{
752 HRESULT hr = S_OK;
753 JET_ERR jEr = JET_errSuccess;
754
755 jEr = JetRollback(jsSession, fAll ? JET_bitRollbackAll : 0);
756 ExitOnJetFailure(jEr, hr, "Failed to rollback transaction");
757
758LExit:
759 return hr;
760}
761
762HRESULT DAPI EseCommitTransaction(
763 __in JET_SESID jsSession
764 )
765{
766 HRESULT hr = S_OK;
767 JET_ERR jEr = JET_errSuccess;
768
769 jEr = JetCommitTransaction(jsSession, 0);
770 ExitOnJetFailure(jEr, hr, "Failed to commit transaction");
771
772LExit:
773 return hr;
774}
775
776HRESULT DAPI EsePrepareUpdate(
777 __in JET_SESID jsSession,
778 __in JET_TABLEID jtTable,
779 __in ULONG ulPrep
780 )
781{
782 HRESULT hr = S_OK;
783 JET_ERR jEr = JET_errSuccess;
784
785 jEr = JetPrepareUpdate(jsSession, jtTable, ulPrep);
786 ExitOnJetFailure(jEr, hr, "Failed to prepare for update of type: %ul", ulPrep);
787
788LExit:
789 return hr;
790}
791
792HRESULT DAPI EseFinishUpdate(
793 __in JET_SESID jsSession,
794 __in JET_TABLEID jtTable,
795 __in BOOL fSeekToInsertedRecord
796 )
797{
798 HRESULT hr = S_OK;
799 JET_ERR jEr = JET_errSuccess;
800 unsigned char rgbBookmark[JET_cbBookmarkMost + 1];
801 DWORD cbBookmark;
802
803 if (fSeekToInsertedRecord)
804 {
805 jEr = JetUpdate(jsSession, jtTable, rgbBookmark, sizeof(rgbBookmark), &cbBookmark);
806 ExitOnJetFailure(jEr, hr, "Failed to run update and retrieve bookmark");
807
808 jEr = JetGotoBookmark(jsSession, jtTable, rgbBookmark, cbBookmark);
809 ExitOnJetFailure(jEr, hr, "Failed to seek to recently updated record using bookmark");
810 }
811 else
812 {
813 jEr = JetUpdate(jsSession, jtTable, NULL, 0, NULL);
814 ExitOnJetFailure(jEr, hr, "Failed to run update (without retrieving bookmark)");
815 }
816
817LExit:
818 // If we fail, the caller won't expect that the update wasn't finished, so we'll cancel their entire update to leave them in a good state
819 if (FAILED(hr))
820 {
821 JetPrepareUpdate(jsSession, jtTable, JET_prepCancel);
822 }
823
824 return hr;
825}
826
827HRESULT DAPI EseSetColumnBinary(
828 __in JET_SESID jsSession,
829 __in ESE_TABLE_SCHEMA tsTable,
830 __in DWORD dwColumn,
831 __in_bcount(cbBuffer) const BYTE* pbBuffer,
832 __in SIZE_T cbBuffer
833 )
834{
835 HRESULT hr = S_OK;
836 JET_ERR jEr = JET_errSuccess;
837
838 jEr = JetSetColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, pbBuffer, static_cast<unsigned long>(cbBuffer), 0, NULL);
839 ExitOnJetFailure(jEr, hr, "Failed to set binary value into column of database");
840
841LExit:
842 return hr;
843}
844
845HRESULT DAPI EseSetColumnDword(
846 __in JET_SESID jsSession,
847 __in ESE_TABLE_SCHEMA tsTable,
848 __in DWORD dwColumn,
849 __in DWORD dwValue
850 )
851{
852 HRESULT hr = S_OK;
853 JET_ERR jEr = JET_errSuccess;
854
855 jEr = JetSetColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, &dwValue, sizeof(DWORD), 0, NULL);
856 ExitOnJetFailure(jEr, hr, "Failed to set dword value into column of database: %u", dwValue);
857
858LExit:
859 return hr;
860}
861
862HRESULT DAPI EseSetColumnBool(
863 __in JET_SESID jsSession,
864 __in ESE_TABLE_SCHEMA tsTable,
865 __in DWORD dwColumn,
866 __in BOOL fValue
867 )
868{
869 HRESULT hr = S_OK;
870 JET_ERR jEr = JET_errSuccess;
871 BYTE bValue = fValue ? 0xFF : 0x00;
872
873 jEr = JetSetColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, &bValue, 1, 0, NULL);
874 ExitOnJetFailure(jEr, hr, "Failed to set bool value into column of database");
875
876LExit:
877 return hr;
878}
879
880HRESULT DAPI EseSetColumnString(
881 __in JET_SESID jsSession,
882 __in ESE_TABLE_SCHEMA tsTable,
883 __in DWORD dwColumn,
884 __in_z LPCWSTR pwzValue
885 )
886{
887 HRESULT hr = S_OK;
888 JET_ERR jEr = JET_errSuccess;
889 size_t cchValue = 0;
890 ULONG cbValueSize = 0;
891
892 if (pwzValue)
893 {
894 hr = ::StringCchLengthW(pwzValue, STRSAFE_MAX_LENGTH, &cchValue);
895 EseExitOnRootFailure(hr, "Failed to get string length: %ls", pwzValue);
896 }
897
898 cbValueSize = static_cast<ULONG>((cchValue + 1) * sizeof(WCHAR)); // add 1 for null character, then multiply by size of WCHAR to get bytes
899
900 jEr = JetSetColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, pwzValue, cbValueSize, 0, NULL);
901 ExitOnJetFailure(jEr, hr, "Failed to set string value into column of database: %ls", pwzValue);
902
903LExit:
904 return hr;
905}
906
907HRESULT DAPI EseSetColumnEmpty(
908 __in JET_SESID jsSession,
909 __in ESE_TABLE_SCHEMA tsTable,
910 __in DWORD dwColumn
911 )
912{
913 HRESULT hr = S_OK;
914 JET_ERR jEr = JET_errSuccess;
915
916 jEr = JetSetColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, NULL, 0, 0, NULL);
917 ExitOnJetFailure(jEr, hr, "Failed to set empty value into column of database");
918
919LExit:
920 return hr;
921}
922
923HRESULT DAPI EseGetColumnBinary(
924 __in JET_SESID jsSession,
925 __in ESE_TABLE_SCHEMA tsTable,
926 __in DWORD dwColumn,
927 __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer,
928 __inout SIZE_T* piBuffer
929 )
930{
931 HRESULT hr = S_OK;
932 JET_ERR jEr = JET_errSuccess;
933 ULONG ulActualSize = 0;
934
935 jEr = JetRetrieveColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, NULL, 0, &ulActualSize, 0, NULL);
936 if (JET_wrnBufferTruncated == jEr)
937 {
938 jEr = JET_errSuccess;
939 }
940 ExitOnJetFailure(jEr, hr, "Failed to check size of binary value from record");
941
942 if (NULL == *ppbBuffer)
943 {
944 *ppbBuffer = reinterpret_cast<BYTE *>(MemAlloc(ulActualSize, FALSE));
945 EseExitOnNull(*ppbBuffer, hr, E_OUTOFMEMORY, "Failed to allocate memory for reading binary value column");
946 }
947 else
948 {
949 *ppbBuffer = reinterpret_cast<BYTE *>(MemReAlloc(*ppbBuffer, ulActualSize, FALSE));
950 EseExitOnNull(*ppbBuffer, hr, E_OUTOFMEMORY, "Failed to reallocate memory for reading binary value column");
951 }
952
953 jEr = JetRetrieveColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, *ppbBuffer, ulActualSize, NULL, 0, NULL);
954 ExitOnJetFailure(jEr, hr, "Failed to retrieve binary value from record");
955
956 *piBuffer = static_cast<SIZE_T>(ulActualSize);
957
958LExit:
959 if (FAILED(hr))
960 {
961 ReleaseNullMem(*ppbBuffer);
962 }
963
964 return hr;
965}
966
967HRESULT DAPI EseGetColumnDword(
968 __in JET_SESID jsSession,
969 __in ESE_TABLE_SCHEMA tsTable,
970 __in DWORD dwColumn,
971 __out DWORD *pdwValue
972 )
973{
974 HRESULT hr = S_OK;
975 JET_ERR jEr = JET_errSuccess;
976
977 jEr = JetRetrieveColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, pdwValue, sizeof(DWORD), NULL, 0, NULL);
978 ExitOnJetFailure(jEr, hr, "Failed to retrieve dword value from record");
979
980LExit:
981 return hr;
982}
983
984HRESULT DAPI EseGetColumnBool(
985 __in JET_SESID jsSession,
986 __in ESE_TABLE_SCHEMA tsTable,
987 __in DWORD dwColumn,
988 __out BOOL *pfValue
989 )
990{
991 HRESULT hr = S_OK;
992 JET_ERR jEr = JET_errSuccess;
993 BYTE bValue = 0;
994
995 jEr = JetRetrieveColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, &bValue, 1, NULL, 0, NULL);
996 ExitOnJetFailure(jEr, hr, "Failed to retrieve bool value from record");
997
998 if (bValue == 0)
999 {
1000 *pfValue = FALSE;
1001 }
1002 else
1003 {
1004 *pfValue = TRUE;
1005 }
1006
1007LExit:
1008 return hr;
1009}
1010
1011HRESULT DAPI EseGetColumnString(
1012 __in JET_SESID jsSession,
1013 __in ESE_TABLE_SCHEMA tsTable,
1014 __in DWORD dwColumn,
1015 __out LPWSTR *ppszValue
1016 )
1017{
1018 HRESULT hr = S_OK;
1019 JET_ERR jEr = JET_errSuccess;
1020 ULONG ulActualSize = 0;
1021
1022 jEr = JetRetrieveColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, NULL, 0, &ulActualSize, 0, NULL);
1023 if (JET_wrnBufferTruncated == jEr)
1024 {
1025 jEr = JET_errSuccess;
1026 }
1027 ExitOnJetFailure(jEr, hr, "Failed to check size of string value from record");
1028
1029 hr = StrAlloc(ppszValue, ulActualSize);
1030 EseExitOnFailure(hr, "Failed to allocate string while retrieving column value");
1031
1032 jEr = JetRetrieveColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, *ppszValue, ulActualSize, NULL, 0, NULL);
1033 ExitOnJetFailure(jEr, hr, "Failed to retrieve string value from record");
1034
1035LExit:
1036 return hr;
1037}
1038
1039HRESULT DAPI EseBeginQuery(
1040 __in JET_SESID jsSession,
1041 __in JET_TABLEID jtTable,
1042 __in ESE_QUERY_TYPE qtQueryType,
1043 __out ESE_QUERY_HANDLE *peqhHandle
1044 )
1045{
1046 UNREFERENCED_PARAMETER(jsSession);
1047 UNREFERENCED_PARAMETER(jtTable);
1048
1049 HRESULT hr = S_OK;
1050
1051 *peqhHandle = static_cast<ESE_QUERY*>(MemAlloc(sizeof(ESE_QUERY), TRUE));
1052 EseExitOnNull(*peqhHandle, hr, E_OUTOFMEMORY, "Failed to allocate new query");
1053
1054 ESE_QUERY *peqHandle = static_cast<ESE_QUERY *>(*peqhHandle);
1055 peqHandle->qtQueryType = qtQueryType;
1056 peqHandle->jsSession = jsSession;
1057 peqHandle->jtTable = jtTable;
1058
1059LExit:
1060 return hr;
1061}
1062
1063// Utility function used by other functions to set a query column
1064HRESULT DAPI SetQueryColumn(
1065 __in ESE_QUERY_HANDLE eqhHandle,
1066 __in_bcount(cbData) const void *pvData,
1067 __in DWORD cbData,
1068 __in JET_GRBIT jGrb
1069 )
1070{
1071 HRESULT hr = S_OK;
1072 JET_ERR jEr = JET_errSuccess;
1073
1074 ESE_QUERY *peqHandle = static_cast<ESE_QUERY *>(eqhHandle);
1075
1076 if (peqHandle->dwColumns == countof(peqHandle->pvData))
1077 {
1078 hr = E_NOTIMPL;
1079 EseExitOnFailure(hr, "Dutil hasn't implemented support for queries of more than %d columns", countof(peqHandle->pvData));
1080 }
1081
1082 if (0 == peqHandle->dwColumns) // If it's the first column, start a new key
1083 {
1084 jGrb = jGrb | JET_bitNewKey;
1085 }
1086
1087 jEr = JetMakeKey(peqHandle->jsSession, peqHandle->jtTable, pvData, cbData, jGrb);
1088 ExitOnJetFailure(jEr, hr, "Failed to begin new query");
1089
1090 // If the query is wildcard, setup the cached copy of pvData
1091 if (ESE_QUERY_EXACT != peqHandle->qtQueryType)
1092 {
1093 peqHandle->pvData[peqHandle->dwColumns] = MemAlloc(cbData, FALSE);
1094 EseExitOnNull(peqHandle->pvData[peqHandle->dwColumns], hr, E_OUTOFMEMORY, "Failed to allocate memory");
1095
1096 memcpy(peqHandle->pvData[peqHandle->dwColumns], pvData, cbData);
1097
1098 peqHandle->cbData[peqHandle->dwColumns] = cbData;
1099 }
1100
1101 // Increment the number of total columns
1102 ++peqHandle->dwColumns;
1103
1104LExit:
1105 return hr;
1106}
1107
1108HRESULT DAPI EseSetQueryColumnBinary(
1109 __in ESE_QUERY_HANDLE eqhHandle,
1110 __in_bcount(cbBuffer) const BYTE* pbBuffer,
1111 __in SIZE_T cbBuffer,
1112 __in BOOL fFinal // If this is true, all other key columns in the query will be set to "*"
1113 )
1114{
1115 HRESULT hr = S_OK;
1116 ESE_QUERY *peqHandle = static_cast<ESE_QUERY *>(eqhHandle);
1117 JET_GRBIT jGrb = 0;
1118
1119 if (cbBuffer > DWORD_MAX)
1120 {
1121 ExitFunction1(hr = E_INVALIDARG);
1122 }
1123
1124 if (fFinal)
1125 {
1126 if (ESE_QUERY_FROM_TOP == peqHandle->qtQueryType)
1127 {
1128 jGrb = jGrb | JET_bitFullColumnStartLimit;
1129 }
1130 else if (ESE_QUERY_FROM_BOTTOM == peqHandle->qtQueryType)
1131 {
1132 jGrb = jGrb | JET_bitFullColumnEndLimit;
1133 }
1134 }
1135
1136 hr = SetQueryColumn(eqhHandle, reinterpret_cast<const void *>(pbBuffer), static_cast<DWORD>(cbBuffer), jGrb);
1137 EseExitOnFailure(hr, "Failed to set value of query colum (as binary) to:");
1138
1139LExit:
1140 return hr;
1141}
1142
1143HRESULT DAPI EseSetQueryColumnDword(
1144 __in ESE_QUERY_HANDLE eqhHandle,
1145 __in DWORD dwData,
1146 __in BOOL fFinal
1147 )
1148{
1149 HRESULT hr = S_OK;
1150 ESE_QUERY *peqHandle = static_cast<ESE_QUERY *>(eqhHandle);
1151 JET_GRBIT jGrb = 0;
1152
1153 if (fFinal)
1154 {
1155 if (ESE_QUERY_FROM_TOP == peqHandle->qtQueryType)
1156 {
1157 jGrb = jGrb | JET_bitFullColumnStartLimit;
1158 }
1159 else if (ESE_QUERY_FROM_BOTTOM == peqHandle->qtQueryType)
1160 {
1161 jGrb = jGrb | JET_bitFullColumnEndLimit;
1162 }
1163 }
1164
1165 hr = SetQueryColumn(eqhHandle, (const void *)&dwData, sizeof(DWORD), jGrb);
1166 EseExitOnFailure(hr, "Failed to set value of query colum (as dword) to: %u", dwData);
1167
1168LExit:
1169 return hr;
1170}
1171
1172HRESULT DAPI EseSetQueryColumnBool(
1173 __in ESE_QUERY_HANDLE eqhHandle,
1174 __in BOOL fValue,
1175 __in BOOL fFinal
1176 )
1177{
1178 HRESULT hr = S_OK;
1179 BYTE bByte = fValue ? 0xFF : 0x00;
1180 ESE_QUERY *peqHandle = static_cast<ESE_QUERY *>(eqhHandle);
1181 JET_GRBIT jGrb = 0;
1182
1183 if (fFinal)
1184 {
1185 if (ESE_QUERY_FROM_TOP == peqHandle->qtQueryType)
1186 {
1187 jGrb = jGrb | JET_bitFullColumnStartLimit;
1188 }
1189 else if (ESE_QUERY_FROM_BOTTOM == peqHandle->qtQueryType)
1190 {
1191 jGrb = jGrb | JET_bitFullColumnEndLimit;
1192 }
1193 }
1194
1195 hr = SetQueryColumn(eqhHandle, (const void *)&bByte, 1, jGrb);
1196 EseExitOnFailure(hr, "Failed to set value of query colum (as bool) to: %s", fValue ? "TRUE" : "FALSE");
1197
1198LExit:
1199 return hr;
1200}
1201
1202HRESULT DAPI EseSetQueryColumnString(
1203 __in ESE_QUERY_HANDLE eqhHandle,
1204 __in_z LPCWSTR pszString,
1205 __in BOOL fFinal
1206 )
1207{
1208 HRESULT hr = S_OK;
1209 DWORD dwStringSize = 0;
1210 size_t cchString = 0;
1211 ESE_QUERY *peqHandle = static_cast<ESE_QUERY *>(eqhHandle);
1212 JET_GRBIT jGrb = 0;
1213
1214 if (pszString)
1215 {
1216 hr = ::StringCchLengthW(pszString, STRSAFE_MAX_LENGTH, &cchString);
1217 EseExitOnRootFailure(hr, "Failed to get size of column string");
1218 }
1219
1220 dwStringSize = static_cast<DWORD>(sizeof(WCHAR) * (cchString + 1)); // Add 1 for null terminator
1221
1222 if (fFinal)
1223 {
1224 if (ESE_QUERY_FROM_TOP == peqHandle->qtQueryType)
1225 {
1226 jGrb = jGrb | JET_bitFullColumnStartLimit;
1227 }
1228 else if (ESE_QUERY_FROM_BOTTOM == peqHandle->qtQueryType)
1229 {
1230 jGrb = jGrb | JET_bitFullColumnEndLimit;
1231 }
1232 }
1233
1234 hr = SetQueryColumn(eqhHandle, (const void *)pszString, dwStringSize, jGrb);
1235 EseExitOnFailure(hr, "Failed to set value of query colum (as string) to: %ls", pszString);
1236
1237LExit:
1238 return hr;
1239}
1240
1241HRESULT DAPI EseFinishQuery(
1242 __in ESE_QUERY_HANDLE eqhHandle
1243 )
1244{
1245 HRESULT hr = S_OK;
1246 JET_ERR jEr = JET_errSuccess;
1247
1248 ESE_QUERY *peqHandle = static_cast<ESE_QUERY *>(eqhHandle);
1249
1250 if (peqHandle->fIndexRangeSet)
1251 {
1252 jEr = JetSetIndexRange(peqHandle->jsSession, peqHandle->jtTable, JET_bitRangeRemove);
1253 ExitOnJetFailure(jEr, hr, "Failed to release index range");
1254
1255 peqHandle->fIndexRangeSet = FALSE;
1256 }
1257
1258 for (int i=0; i < countof(peqHandle->pvData); ++i)
1259 {
1260 ReleaseMem(peqHandle->pvData[i]);
1261 }
1262
1263 ReleaseMem(peqHandle);
1264
1265LExit:
1266 return hr;
1267}
1268
1269HRESULT DAPI EseRunQuery(
1270 __in ESE_QUERY_HANDLE eqhHandle
1271 )
1272{
1273 HRESULT hr = S_OK;
1274 JET_ERR jEr = JET_errSuccess;
1275 JET_GRBIT jGrb = 0;
1276 JET_GRBIT jGrbSeekType = 0;
1277 DWORD i;
1278
1279 ESE_QUERY *peqHandle = static_cast<ESE_QUERY *>(eqhHandle);
1280
1281 if (ESE_QUERY_EXACT == peqHandle->qtQueryType)
1282 {
1283 jEr = JetSeek(peqHandle->jsSession, peqHandle->jtTable, JET_bitSeekEQ);
1284 ExitOnJetFailure(jEr, hr, "Failed to seek EQ within jet table");
1285 }
1286 else
1287 {
1288 if (ESE_QUERY_FROM_TOP == peqHandle->qtQueryType)
1289 {
1290 jGrbSeekType = JET_bitSeekGE;
1291 }
1292 else if (ESE_QUERY_FROM_BOTTOM == peqHandle->qtQueryType)
1293 {
1294 jGrbSeekType = JET_bitSeekLE;
1295 }
1296
1297 jEr = JetSeek(peqHandle->jsSession, peqHandle->jtTable, jGrbSeekType);
1298 if (jEr == JET_wrnSeekNotEqual)
1299 {
1300 jEr = JET_errSuccess;
1301 }
1302
1303 // At this point we've already set our cursor to the beginning of the range of records to select.
1304 // Now we'll make a key pointing to the end of the range of records to select, so we can call JetSetIndexRange()
1305 // For a semi-explanation, see this doc page: http://msdn.microsoft.com/en-us/library/aa964799%28EXCHG.10%29.aspx
1306 for (i = 0; i < peqHandle->dwColumns; ++i)
1307 {
1308 if (i == 0)
1309 {
1310 jGrb = JET_bitNewKey;
1311 }
1312 else
1313 {
1314 jGrb = 0;
1315 }
1316
1317 // On the last iteration
1318 if (i == peqHandle->dwColumns - 1)
1319 {
1320 jGrb |= JET_bitFullColumnEndLimit;
1321 }
1322
1323 jEr = JetMakeKey(peqHandle->jsSession, peqHandle->jtTable, peqHandle->pvData[i], peqHandle->cbData[i], jGrb);
1324 ExitOnJetFailure(jEr, hr, "Failed to begin new query");
1325 }
1326
1327 jEr = JetSetIndexRange(peqHandle->jsSession, peqHandle->jtTable, JET_bitRangeUpperLimit);
1328 ExitOnJetFailure(jEr, hr, "Failed to set index range");
1329
1330 peqHandle->fIndexRangeSet = TRUE;
1331
1332 // Sometimes JetBlue doesn't check if there is a current record when calling the above function (and sometimes it does)
1333 // So, let's check if there is a current record before returning (by reading the first byte of one).
1334 jEr = JetMove(peqHandle->jsSession, peqHandle->jtTable, 0, 0);
1335 ExitOnJetFailure(jEr, hr, "Failed to check if there is a current record after query");
1336 }
1337
1338LExit:
1339 return hr;
1340}
diff --git a/src/libs/dutil/WixToolset.DUtil/fileutil.cpp b/src/libs/dutil/WixToolset.DUtil/fileutil.cpp
new file mode 100644
index 00000000..1822727a
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/fileutil.cpp
@@ -0,0 +1,2032 @@
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// Exit macros
7#define FileExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__)
8#define FileExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__)
9#define FileExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__)
10#define FileExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__)
11#define FileExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__)
12#define FileExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__)
13#define FileExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_FILEUTIL, p, x, e, s, __VA_ARGS__)
14#define FileExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_FILEUTIL, p, x, s, __VA_ARGS__)
15#define FileExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_FILEUTIL, p, x, e, s, __VA_ARGS__)
16#define FileExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_FILEUTIL, p, x, s, __VA_ARGS__)
17#define FileExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_FILEUTIL, e, x, s, __VA_ARGS__)
18#define FileExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_FILEUTIL, g, x, s, __VA_ARGS__)
19
20// constants
21
22const BYTE UTF8BOM[] = {0xEF, 0xBB, 0xBF};
23const BYTE UTF16BOM[] = {0xFF, 0xFE};
24
25const LPCWSTR REGISTRY_PENDING_FILE_RENAME_KEY = L"SYSTEM\\CurrentControlSet\\Control\\Session Manager";
26const LPCWSTR REGISTRY_PENDING_FILE_RENAME_VALUE = L"PendingFileRenameOperations";
27
28/*******************************************************************
29 FileFromPath - returns a pointer to the file part of the path
30
31********************************************************************/
32extern "C" LPWSTR DAPI FileFromPath(
33 __in_z LPCWSTR wzPath
34 )
35{
36 if (!wzPath)
37 return NULL;
38
39 LPWSTR wzFile = const_cast<LPWSTR>(wzPath);
40 for (LPWSTR wz = wzFile; *wz; ++wz)
41 {
42 // valid delineators
43 // \ => Windows path
44 // / => unix and URL path
45 // : => relative path from mapped root
46 if (L'\\' == *wz || L'/' == *wz || L':' == *wz)
47 wzFile = wz + 1;
48 }
49
50 return wzFile;
51}
52
53
54/*******************************************************************
55 FileResolvePath - gets the full path to a file resolving environment
56 variables along the way.
57
58********************************************************************/
59extern "C" HRESULT DAPI FileResolvePath(
60 __in_z LPCWSTR wzRelativePath,
61 __out LPWSTR *ppwzFullPath
62 )
63{
64 Assert(wzRelativePath && *wzRelativePath);
65
66 HRESULT hr = S_OK;
67 DWORD cch = 0;
68 LPWSTR pwzExpandedPath = NULL;
69 DWORD cchExpandedPath = 0;
70
71 LPWSTR pwzFullPath = NULL;
72 DWORD cchFullPath = 0;
73
74 LPWSTR wzFileName = NULL;
75
76 //
77 // First, expand any environment variables.
78 //
79 cchExpandedPath = MAX_PATH;
80 hr = StrAlloc(&pwzExpandedPath, cchExpandedPath);
81 FileExitOnFailure(hr, "Failed to allocate space for expanded path.");
82
83 cch = ::ExpandEnvironmentStringsW(wzRelativePath, pwzExpandedPath, cchExpandedPath);
84 if (0 == cch)
85 {
86 FileExitWithLastError(hr, "Failed to expand environment variables in string: %ls", wzRelativePath);
87 }
88 else if (cchExpandedPath < cch)
89 {
90 cchExpandedPath = cch;
91 hr = StrAlloc(&pwzExpandedPath, cchExpandedPath);
92 FileExitOnFailure(hr, "Failed to re-allocate more space for expanded path.");
93
94 cch = ::ExpandEnvironmentStringsW(wzRelativePath, pwzExpandedPath, cchExpandedPath);
95 if (0 == cch)
96 {
97 FileExitWithLastError(hr, "Failed to expand environment variables in string: %ls", wzRelativePath);
98 }
99 else if (cchExpandedPath < cch)
100 {
101 hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
102 FileExitOnRootFailure(hr, "Failed to allocate buffer for expanded path.");
103 }
104 }
105
106 //
107 // Second, get the full path.
108 //
109 cchFullPath = MAX_PATH;
110 hr = StrAlloc(&pwzFullPath, cchFullPath);
111 FileExitOnFailure(hr, "Failed to allocate space for full path.");
112
113 cch = ::GetFullPathNameW(pwzExpandedPath, cchFullPath, pwzFullPath, &wzFileName);
114 if (0 == cch)
115 {
116 FileExitWithLastError(hr, "Failed to get full path for string: %ls", pwzExpandedPath);
117 }
118 else if (cchFullPath < cch)
119 {
120 cchFullPath = cch;
121 hr = StrAlloc(&pwzFullPath, cchFullPath);
122 FileExitOnFailure(hr, "Failed to re-allocate more space for full path.");
123
124 cch = ::GetFullPathNameW(pwzExpandedPath, cchFullPath, pwzFullPath, &wzFileName);
125 if (0 == cch)
126 {
127 FileExitWithLastError(hr, "Failed to get full path for string: %ls", pwzExpandedPath);
128 }
129 else if (cchFullPath < cch)
130 {
131 hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
132 FileExitOnRootFailure(hr, "Failed to allocate buffer for full path.");
133 }
134 }
135
136 *ppwzFullPath = pwzFullPath;
137 pwzFullPath = NULL;
138
139LExit:
140 ReleaseStr(pwzFullPath);
141 ReleaseStr(pwzExpandedPath);
142
143 return hr;
144}
145
146
147/*******************************************************************
148FileStripExtension - Strip extension from filename
149********************************************************************/
150extern "C" HRESULT DAPI FileStripExtension(
151__in_z LPCWSTR wzFileName,
152__out LPWSTR *ppwzFileNameNoExtension
153)
154{
155 Assert(wzFileName && *wzFileName);
156
157 HRESULT hr = S_OK;
158 size_t cchFileName = 0;
159 LPWSTR pwzFileNameNoExtension = NULL;
160 size_t cchFileNameNoExtension = 0;
161 errno_t err = 0;
162
163 hr = ::StringCchLengthW(wzFileName, STRSAFE_MAX_LENGTH, &cchFileName);
164 FileExitOnRootFailure(hr, "failed to get length of file name: %ls", wzFileName);
165
166 cchFileNameNoExtension = cchFileName + 1;
167
168 hr = StrAlloc(&pwzFileNameNoExtension, cchFileNameNoExtension);
169 FileExitOnFailure(hr, "failed to allocate space for File Name without extension");
170
171 // _wsplitpath_s can handle drive/path/filename/extension
172 err = _wsplitpath_s(wzFileName, NULL, NULL, NULL, NULL, pwzFileNameNoExtension, cchFileNameNoExtension, NULL, NULL);
173 if (err)
174 {
175 hr = E_INVALIDARG;
176 FileExitOnRootFailure(hr, "failed to parse filename: '%ls', error: %d", wzFileName, err);
177 }
178
179 *ppwzFileNameNoExtension = pwzFileNameNoExtension;
180 pwzFileNameNoExtension = NULL;
181
182LExit:
183 ReleaseStr(pwzFileNameNoExtension);
184
185 return hr;
186}
187
188
189/*******************************************************************
190FileChangeExtension - Changes the extension of a filename
191********************************************************************/
192extern "C" HRESULT DAPI FileChangeExtension(
193 __in_z LPCWSTR wzFileName,
194 __in_z LPCWSTR wzNewExtension,
195 __out LPWSTR *ppwzFileNameNewExtension
196 )
197{
198 Assert(wzFileName && *wzFileName);
199
200 HRESULT hr = S_OK;
201 LPWSTR sczFileName = NULL;
202
203 hr = FileStripExtension(wzFileName, &sczFileName);
204 FileExitOnFailure(hr, "Failed to strip extension from file name: %ls", wzFileName);
205
206 hr = StrAllocConcat(&sczFileName, wzNewExtension, 0);
207 FileExitOnFailure(hr, "Failed to add new extension.");
208
209 *ppwzFileNameNewExtension = sczFileName;
210 sczFileName = NULL;
211
212LExit:
213 ReleaseStr(sczFileName);
214
215 return hr;
216}
217
218
219/*******************************************************************
220FileAddSuffixToBaseName - Adds a suffix the base portion of a file
221name; e.g., file.ext to fileSuffix.ext.
222********************************************************************/
223extern "C" HRESULT DAPI FileAddSuffixToBaseName(
224 __in_z LPCWSTR wzFileName,
225 __in_z LPCWSTR wzSuffix,
226 __out_z LPWSTR* psczNewFileName
227 )
228{
229 Assert(wzFileName && *wzFileName);
230
231 HRESULT hr = S_OK;
232 LPWSTR sczNewFileName = NULL;
233 size_t cchFileName = 0;
234
235 hr = ::StringCchLengthW(wzFileName, STRSAFE_MAX_CCH, &cchFileName);
236 FileExitOnRootFailure(hr, "Failed to get length of file name: %ls", wzFileName);
237
238 LPCWSTR wzExtension = wzFileName + cchFileName;
239 while (wzFileName < wzExtension && L'.' != *wzExtension)
240 {
241 --wzExtension;
242 }
243
244 if (wzFileName < wzExtension)
245 {
246 // found an extension so add the suffix before it
247 hr = StrAllocFormatted(&sczNewFileName, L"%.*ls%ls%ls", static_cast<int>(wzExtension - wzFileName), wzFileName, wzSuffix, wzExtension);
248 }
249 else
250 {
251 // no extension, so add the suffix at the end of the whole name
252 hr = StrAllocString(&sczNewFileName, wzFileName, 0);
253 FileExitOnFailure(hr, "Failed to allocate new file name.");
254
255 hr = StrAllocConcat(&sczNewFileName, wzSuffix, 0);
256 }
257 FileExitOnFailure(hr, "Failed to allocate new file name with suffix.");
258
259 *psczNewFileName = sczNewFileName;
260 sczNewFileName = NULL;
261
262LExit:
263 ReleaseStr(sczNewFileName);
264
265 return hr;
266}
267
268
269/*******************************************************************
270 FileVersion
271
272********************************************************************/
273extern "C" HRESULT DAPI FileVersion(
274 __in_z LPCWSTR wzFilename,
275 __out DWORD *pdwVerMajor,
276 __out DWORD* pdwVerMinor
277 )
278{
279 HRESULT hr = S_OK;
280
281 DWORD dwHandle = 0;
282 UINT cbVerBuffer = 0;
283 LPVOID pVerBuffer = NULL;
284 VS_FIXEDFILEINFO* pvsFileInfo = NULL;
285 UINT cbFileInfo = 0;
286
287 if (0 == (cbVerBuffer = ::GetFileVersionInfoSizeW(wzFilename, &dwHandle)))
288 {
289 FileExitOnLastErrorDebugTrace(hr, "failed to get version info for file: %ls", wzFilename);
290 }
291
292 pVerBuffer = ::GlobalAlloc(GMEM_FIXED, cbVerBuffer);
293 FileExitOnNullDebugTrace(pVerBuffer, hr, E_OUTOFMEMORY, "failed to allocate version info for file: %ls", wzFilename);
294
295 if (!::GetFileVersionInfoW(wzFilename, dwHandle, cbVerBuffer, pVerBuffer))
296 {
297 FileExitOnLastErrorDebugTrace(hr, "failed to get version info for file: %ls", wzFilename);
298 }
299
300 if (!::VerQueryValueW(pVerBuffer, L"\\", (void**)&pvsFileInfo, &cbFileInfo))
301 {
302 FileExitOnLastErrorDebugTrace(hr, "failed to get version value for file: %ls", wzFilename);
303 }
304
305 *pdwVerMajor = pvsFileInfo->dwFileVersionMS;
306 *pdwVerMinor = pvsFileInfo->dwFileVersionLS;
307
308LExit:
309 if (pVerBuffer)
310 {
311 ::GlobalFree(pVerBuffer);
312 }
313 return hr;
314}
315
316
317/*******************************************************************
318 FileVersionFromString
319
320*******************************************************************/
321extern "C" HRESULT DAPI FileVersionFromString(
322 __in_z LPCWSTR wzVersion,
323 __out DWORD* pdwVerMajor,
324 __out DWORD* pdwVerMinor
325 )
326{
327 Assert(pdwVerMajor && pdwVerMinor);
328
329 HRESULT hr = S_OK;
330 LPCWSTR pwz = wzVersion;
331 DWORD dw;
332
333 *pdwVerMajor = 0;
334 *pdwVerMinor = 0;
335
336 if ((L'v' == *pwz) || (L'V' == *pwz))
337 {
338 ++pwz;
339 }
340
341 dw = wcstoul(pwz, (WCHAR**)&pwz, 10);
342 if (pwz && (L'.' == *pwz && dw < 0x10000) || !*pwz)
343 {
344 *pdwVerMajor = dw << 16;
345
346 if (!*pwz)
347 {
348 ExitFunction1(hr = S_OK);
349 }
350 ++pwz;
351 }
352 else
353 {
354 ExitFunction1(hr = S_FALSE);
355 }
356
357 dw = wcstoul(pwz, (WCHAR**)&pwz, 10);
358 if (pwz && (L'.' == *pwz && dw < 0x10000) || !*pwz)
359 {
360 *pdwVerMajor |= dw;
361
362 if (!*pwz)
363 {
364 ExitFunction1(hr = S_OK);
365 }
366 ++pwz;
367 }
368 else
369 {
370 ExitFunction1(hr = S_FALSE);
371 }
372
373 dw = wcstoul(pwz, (WCHAR**)&pwz, 10);
374 if (pwz && (L'.' == *pwz && dw < 0x10000) || !*pwz)
375 {
376 *pdwVerMinor = dw << 16;
377
378 if (!*pwz)
379 {
380 ExitFunction1(hr = S_OK);
381 }
382 ++pwz;
383 }
384 else
385 {
386 ExitFunction1(hr = S_FALSE);
387 }
388
389 dw = wcstoul(pwz, (WCHAR**)&pwz, 10);
390 if (pwz && L'\0' == *pwz && dw < 0x10000)
391 {
392 *pdwVerMinor |= dw;
393 }
394 else
395 {
396 ExitFunction1(hr = S_FALSE);
397 }
398
399LExit:
400 return hr;
401}
402
403
404/*******************************************************************
405 FileVersionFromStringEx
406
407*******************************************************************/
408extern "C" HRESULT DAPI FileVersionFromStringEx(
409 __in_z LPCWSTR wzVersion,
410 __in SIZE_T cchVersion,
411 __out DWORD64* pqwVersion
412 )
413{
414 Assert(wzVersion);
415 Assert(pqwVersion);
416
417 HRESULT hr = S_OK;
418 LPCWSTR wzEnd = NULL;
419 LPCWSTR wzPartBegin = wzVersion;
420 LPCWSTR wzPartEnd = wzVersion;
421 DWORD iPart = 0;
422 USHORT us = 0;
423 DWORD64 qwVersion = 0;
424
425 // get string length if not provided
426 if (0 >= cchVersion)
427 {
428 hr = ::StringCchLengthW(wzVersion, STRSAFE_MAX_CCH, reinterpret_cast<size_t*>(&cchVersion));
429 FileExitOnRootFailure(hr, "Failed to get length of file version string: %ls", wzVersion);
430
431 if (0 >= cchVersion)
432 {
433 ExitFunction1(hr = E_INVALIDARG);
434 }
435 }
436
437 if ((L'v' == *wzVersion) || (L'V' == *wzVersion))
438 {
439 ++wzVersion;
440 --cchVersion;
441 wzPartBegin = wzVersion;
442 wzPartEnd = wzVersion;
443 }
444
445 // save end pointer
446 wzEnd = wzVersion + cchVersion;
447
448 // loop through parts
449 for (;;)
450 {
451 if (4 <= iPart)
452 {
453 // error, too many parts
454 ExitFunction1(hr = E_INVALIDARG);
455 }
456
457 // find end of part
458 while (wzPartEnd < wzEnd && L'.' != *wzPartEnd)
459 {
460 ++wzPartEnd;
461 }
462 if (wzPartBegin == wzPartEnd)
463 {
464 // error, empty part
465 ExitFunction1(hr = E_INVALIDARG);
466 }
467
468 DWORD cchPart;
469 hr = ::PtrdiffTToDWord(wzPartEnd - wzPartBegin, &cchPart);
470 FileExitOnFailure(hr, "Version number part was too long.");
471
472 // parse version part
473 hr = StrStringToUInt16(wzPartBegin, cchPart, &us);
474 FileExitOnFailure(hr, "Failed to parse version number part.");
475
476 // add part to qword version
477 qwVersion |= (DWORD64)us << ((3 - iPart) * 16);
478
479 if (wzPartEnd >= wzEnd)
480 {
481 // end of string
482 break;
483 }
484
485 wzPartBegin = ++wzPartEnd; // skip over separator
486 ++iPart;
487 }
488
489 *pqwVersion = qwVersion;
490
491LExit:
492 return hr;
493}
494
495/*******************************************************************
496 FileVersionFromStringEx - Formats the DWORD64 as a string version.
497
498*******************************************************************/
499extern "C" HRESULT DAPI FileVersionToStringEx(
500 __in DWORD64 qwVersion,
501 __out LPWSTR* psczVersion
502 )
503{
504 HRESULT hr = S_OK;
505 WORD wMajor = 0;
506 WORD wMinor = 0;
507 WORD wBuild = 0;
508 WORD wRevision = 0;
509
510 // Mask and shift each WORD for each field.
511 wMajor = (WORD)(qwVersion >> 48 & 0xffff);
512 wMinor = (WORD)(qwVersion >> 32 & 0xffff);
513 wBuild = (WORD)(qwVersion >> 16 & 0xffff);
514 wRevision = (WORD)(qwVersion & 0xffff);
515
516 // Format and return the version string.
517 hr = StrAllocFormatted(psczVersion, L"%u.%u.%u.%u", wMajor, wMinor, wBuild, wRevision);
518 FileExitOnFailure(hr, "Failed to allocate and format the version number.");
519
520LExit:
521 return hr;
522}
523
524/*******************************************************************
525 FileSetPointer - sets the file pointer.
526
527********************************************************************/
528extern "C" HRESULT DAPI FileSetPointer(
529 __in HANDLE hFile,
530 __in DWORD64 dw64Move,
531 __out_opt DWORD64* pdw64NewPosition,
532 __in DWORD dwMoveMethod
533 )
534{
535 Assert(INVALID_HANDLE_VALUE != hFile);
536
537 HRESULT hr = S_OK;
538 LARGE_INTEGER liMove;
539 LARGE_INTEGER liNewPosition;
540
541 liMove.QuadPart = dw64Move;
542 if (!::SetFilePointerEx(hFile, liMove, &liNewPosition, dwMoveMethod))
543 {
544 FileExitWithLastError(hr, "Failed to set file pointer.");
545 }
546
547 if (pdw64NewPosition)
548 {
549 *pdw64NewPosition = liNewPosition.QuadPart;
550 }
551
552LExit:
553 return hr;
554}
555
556
557/*******************************************************************
558 FileSize
559
560********************************************************************/
561extern "C" HRESULT DAPI FileSize(
562 __in_z LPCWSTR pwzFileName,
563 __out LONGLONG* pllSize
564 )
565{
566 HRESULT hr = S_OK;
567 HANDLE hFile = INVALID_HANDLE_VALUE;
568
569 FileExitOnNull(pwzFileName, hr, E_INVALIDARG, "Attempted to check filename, but no filename was provided");
570
571 hFile = ::CreateFileW(pwzFileName, FILE_READ_ATTRIBUTES, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
572 if (INVALID_HANDLE_VALUE == hFile)
573 {
574 FileExitWithLastError(hr, "Failed to open file %ls while checking file size", pwzFileName);
575 }
576
577 hr = FileSizeByHandle(hFile, pllSize);
578 FileExitOnFailure(hr, "Failed to check size of file %ls by handle", pwzFileName);
579
580LExit:
581 ReleaseFileHandle(hFile);
582
583 return hr;
584}
585
586
587/*******************************************************************
588 FileSizeByHandle
589
590********************************************************************/
591extern "C" HRESULT DAPI FileSizeByHandle(
592 __in HANDLE hFile,
593 __out LONGLONG* pllSize
594 )
595{
596 Assert(INVALID_HANDLE_VALUE != hFile && pllSize);
597 HRESULT hr = S_OK;
598 LARGE_INTEGER li;
599
600 *pllSize = 0;
601
602 if (!::GetFileSizeEx(hFile, &li))
603 {
604 FileExitWithLastError(hr, "Failed to get size of file.");
605 }
606
607 *pllSize = li.QuadPart;
608
609LExit:
610 return hr;
611}
612
613
614/*******************************************************************
615 FileExistsEx
616
617********************************************************************/
618extern "C" BOOL DAPI FileExistsEx(
619 __in_z LPCWSTR wzPath,
620 __out_opt DWORD *pdwAttributes
621 )
622{
623 Assert(wzPath && *wzPath);
624 BOOL fExists = FALSE;
625
626 WIN32_FIND_DATAW fd = { };
627 HANDLE hff;
628
629 if (INVALID_HANDLE_VALUE != (hff = ::FindFirstFileW(wzPath, &fd)))
630 {
631 ::FindClose(hff);
632 if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
633 {
634 if (pdwAttributes)
635 {
636 *pdwAttributes = fd.dwFileAttributes;
637 }
638
639 fExists = TRUE;
640 }
641 }
642
643 return fExists;
644}
645
646
647/*******************************************************************
648 FileExistsAfterRestart - checks that a file exists and will continue
649 to exist after restart.
650
651********************************************************************/
652extern "C" BOOL DAPI FileExistsAfterRestart(
653 __in_z LPCWSTR wzPath,
654 __out_opt DWORD *pdwAttributes
655 )
656{
657 HRESULT hr = S_OK;
658 BOOL fExists = FALSE;
659 HKEY hkPendingFileRename = NULL;
660 LPWSTR* rgsczRenames = NULL;
661 DWORD cRenames = 0;
662 int nCompare = 0;
663
664 fExists = FileExistsEx(wzPath, pdwAttributes);
665 if (fExists)
666 {
667 hr = RegOpen(HKEY_LOCAL_MACHINE, REGISTRY_PENDING_FILE_RENAME_KEY, KEY_QUERY_VALUE, &hkPendingFileRename);
668 if (E_FILENOTFOUND == hr)
669 {
670 ExitFunction1(hr = S_OK);
671 }
672 FileExitOnFailure(hr, "Failed to open pending file rename registry key.");
673
674 hr = RegReadStringArray(hkPendingFileRename, REGISTRY_PENDING_FILE_RENAME_VALUE, &rgsczRenames, &cRenames);
675 if (E_FILENOTFOUND == hr)
676 {
677 ExitFunction1(hr = S_OK);
678 }
679 FileExitOnFailure(hr, "Failed to read pending file renames.");
680
681 // The pending file renames array is pairs of source and target paths. We only care
682 // about checking the source paths so skip the target paths (i += 2).
683 for (DWORD i = 0; i < cRenames; i += 2)
684 {
685 LPWSTR wzRename = rgsczRenames[i];
686 if (wzRename && *wzRename)
687 {
688 // Skip the long path designator if present.
689 if (L'\\' == wzRename[0] && L'?' == wzRename[1] && L'?' == wzRename[2] && L'\\' == wzRename[3])
690 {
691 wzRename += 4;
692 }
693
694 hr = PathCompare(wzPath, wzRename, &nCompare);
695 FileExitOnFailure(hr, "Failed to compare path from pending file rename to check path.");
696
697 if (CSTR_EQUAL == nCompare)
698 {
699 fExists = FALSE;
700 break;
701 }
702 }
703 }
704 }
705
706LExit:
707 ReleaseStrArray(rgsczRenames, cRenames);
708 ReleaseRegKey(hkPendingFileRename);
709
710 return fExists;
711}
712
713
714/*******************************************************************
715 FileRemoveFromPendingRename - removes the file path from the pending
716 file rename list.
717
718********************************************************************/
719extern "C" HRESULT DAPI FileRemoveFromPendingRename(
720 __in_z LPCWSTR wzPath
721 )
722{
723 HRESULT hr = S_OK;
724 HKEY hkPendingFileRename = NULL;
725 LPWSTR* rgsczRenames = NULL;
726 DWORD cRenames = 0;
727 int nCompare = 0;
728 BOOL fRemoved = FALSE;
729 DWORD cNewRenames = 0;
730
731 hr = RegOpen(HKEY_LOCAL_MACHINE, REGISTRY_PENDING_FILE_RENAME_KEY, KEY_QUERY_VALUE | KEY_SET_VALUE, &hkPendingFileRename);
732 if (E_FILENOTFOUND == hr)
733 {
734 ExitFunction1(hr = S_OK);
735 }
736 FileExitOnFailure(hr, "Failed to open pending file rename registry key.");
737
738 hr = RegReadStringArray(hkPendingFileRename, REGISTRY_PENDING_FILE_RENAME_VALUE, &rgsczRenames, &cRenames);
739 if (E_FILENOTFOUND == hr)
740 {
741 ExitFunction1(hr = S_OK);
742 }
743 FileExitOnFailure(hr, "Failed to read pending file renames.");
744
745 // The pending file renames array is pairs of source and target paths. We only care
746 // about checking the source paths so skip the target paths (i += 2).
747 for (DWORD i = 0; i < cRenames; i += 2)
748 {
749 LPWSTR wzRename = rgsczRenames[i];
750 if (wzRename && *wzRename)
751 {
752 // Skip the long path designator if present.
753 if (L'\\' == wzRename[0] && L'?' == wzRename[1] && L'?' == wzRename[2] && L'\\' == wzRename[3])
754 {
755 wzRename += 4;
756 }
757
758 hr = PathCompare(wzPath, wzRename, &nCompare);
759 FileExitOnFailure(hr, "Failed to compare path from pending file rename to check path.");
760
761 // If we find our path in the list, null out the source and target slot and
762 // we'll compact the array next.
763 if (CSTR_EQUAL == nCompare)
764 {
765 ReleaseNullStr(rgsczRenames[i]);
766 ReleaseNullStr(rgsczRenames[i + 1]);
767 fRemoved = TRUE;
768 }
769 }
770 }
771
772 if (fRemoved)
773 {
774 // Compact the array by removing any nulls.
775 for (DWORD i = 0; i < cRenames; ++i)
776 {
777 LPWSTR wzRename = rgsczRenames[i];
778 if (wzRename)
779 {
780 rgsczRenames[cNewRenames] = wzRename;
781 ++cNewRenames;
782 }
783 }
784
785 cRenames = cNewRenames; // ignore the pointers on the end of the array since an early index points to them already.
786
787 // Write the new array back to the pending file rename key.
788 hr = RegWriteStringArray(hkPendingFileRename, REGISTRY_PENDING_FILE_RENAME_VALUE, rgsczRenames, cRenames);
789 FileExitOnFailure(hr, "Failed to update pending file renames.");
790 }
791
792LExit:
793 ReleaseStrArray(rgsczRenames, cRenames);
794 ReleaseRegKey(hkPendingFileRename);
795
796 return hr;
797}
798
799
800/*******************************************************************
801 FileRead - read a file into memory
802
803********************************************************************/
804extern "C" HRESULT DAPI FileRead(
805 __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest,
806 __out SIZE_T* pcbDest,
807 __in_z LPCWSTR wzSrcPath
808 )
809{
810 HRESULT hr = FileReadPartial(ppbDest, pcbDest, wzSrcPath, FALSE, 0, 0xFFFFFFFF, FALSE);
811 return hr;
812}
813
814/*******************************************************************
815 FileRead - read a file into memory with specified share mode
816
817********************************************************************/
818extern "C" HRESULT DAPI FileReadEx(
819 __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest,
820 __out SIZE_T* pcbDest,
821 __in_z LPCWSTR wzSrcPath,
822 __in DWORD dwShareMode
823 )
824{
825 HRESULT hr = FileReadPartialEx(ppbDest, pcbDest, wzSrcPath, FALSE, 0, 0xFFFFFFFF, FALSE, dwShareMode);
826 return hr;
827}
828
829/*******************************************************************
830 FileReadUntil - read a file into memory with a maximum size
831
832********************************************************************/
833extern "C" HRESULT DAPI FileReadUntil(
834 __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest,
835 __out_range(<=, cbMaxRead) SIZE_T* pcbDest,
836 __in_z LPCWSTR wzSrcPath,
837 __in DWORD cbMaxRead
838 )
839{
840 HRESULT hr = FileReadPartial(ppbDest, pcbDest, wzSrcPath, FALSE, 0, cbMaxRead, FALSE);
841 return hr;
842}
843
844
845/*******************************************************************
846 FileReadPartial - read a portion of a file into memory
847
848********************************************************************/
849extern "C" HRESULT DAPI FileReadPartial(
850 __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest,
851 __out_range(<=, cbMaxRead) SIZE_T* pcbDest,
852 __in_z LPCWSTR wzSrcPath,
853 __in BOOL fSeek,
854 __in DWORD cbStartPosition,
855 __in DWORD cbMaxRead,
856 __in BOOL fPartialOK
857 )
858{
859 return FileReadPartialEx(ppbDest, pcbDest, wzSrcPath, fSeek, cbStartPosition, cbMaxRead, fPartialOK, FILE_SHARE_READ | FILE_SHARE_DELETE);
860}
861
862/*******************************************************************
863 FileReadPartial - read a portion of a file into memory
864 (with specified share mode)
865********************************************************************/
866extern "C" HRESULT DAPI FileReadPartialEx(
867 __deref_inout_bcount_full(*pcbDest) LPBYTE* ppbDest,
868 __out_range(<=, cbMaxRead) SIZE_T* pcbDest,
869 __in_z LPCWSTR wzSrcPath,
870 __in BOOL fSeek,
871 __in DWORD cbStartPosition,
872 __in DWORD cbMaxRead,
873 __in BOOL fPartialOK,
874 __in DWORD dwShareMode
875 )
876{
877 HRESULT hr = S_OK;
878
879 UINT er = ERROR_SUCCESS;
880 HANDLE hFile = INVALID_HANDLE_VALUE;
881 LARGE_INTEGER liFileSize = { };
882 DWORD cbData = 0;
883 BYTE* pbData = NULL;
884
885 FileExitOnNull(pcbDest, hr, E_INVALIDARG, "Invalid argument pcbDest");
886 FileExitOnNull(ppbDest, hr, E_INVALIDARG, "Invalid argument ppbDest");
887 FileExitOnNull(wzSrcPath, hr, E_INVALIDARG, "Invalid argument wzSrcPath");
888 FileExitOnNull(*wzSrcPath, hr, E_INVALIDARG, "*wzSrcPath is null");
889
890 hFile = ::CreateFileW(wzSrcPath, GENERIC_READ, dwShareMode, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
891 if (INVALID_HANDLE_VALUE == hFile)
892 {
893 er = ::GetLastError();
894 if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er))
895 {
896 ExitFunction1(hr = E_FILENOTFOUND);
897 }
898 FileExitOnWin32Error(er, hr, "Failed to open file: %ls", wzSrcPath);
899 }
900
901 if (!::GetFileSizeEx(hFile, &liFileSize))
902 {
903 FileExitWithLastError(hr, "Failed to get size of file: %ls", wzSrcPath);
904 }
905
906 if (fSeek)
907 {
908 if (cbStartPosition > liFileSize.QuadPart)
909 {
910 hr = E_INVALIDARG;
911 FileExitOnFailure(hr, "Start position %d bigger than file '%ls' size %llu", cbStartPosition, wzSrcPath, liFileSize.QuadPart);
912 }
913
914 DWORD dwErr = ::SetFilePointer(hFile, cbStartPosition, NULL, FILE_CURRENT);
915 if (INVALID_SET_FILE_POINTER == dwErr)
916 {
917 FileExitOnLastError(hr, "Failed to seek position %d", cbStartPosition);
918 }
919 }
920 else
921 {
922 cbStartPosition = 0;
923 }
924
925 if (fPartialOK)
926 {
927 cbData = cbMaxRead;
928 }
929 else
930 {
931 cbData = liFileSize.LowPart - cbStartPosition; // should only need the low part because we cap at DWORD
932 if (cbMaxRead < liFileSize.QuadPart - cbStartPosition)
933 {
934 hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
935 FileExitOnRootFailure(hr, "Failed to load file: %ls, too large.", wzSrcPath);
936 }
937 }
938
939 if (*ppbDest)
940 {
941 if (0 == cbData)
942 {
943 ReleaseNullMem(*ppbDest);
944 *pcbDest = 0;
945 ExitFunction1(hr = S_OK);
946 }
947
948 LPVOID pv = MemReAlloc(*ppbDest, cbData, TRUE);
949 FileExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to re-allocate memory to read in file: %ls", wzSrcPath);
950
951 pbData = static_cast<BYTE*>(pv);
952 }
953 else
954 {
955 if (0 == cbData)
956 {
957 *pcbDest = 0;
958 ExitFunction1(hr = S_OK);
959 }
960
961 pbData = static_cast<BYTE*>(MemAlloc(cbData, TRUE));
962 FileExitOnNull(pbData, hr, E_OUTOFMEMORY, "Failed to allocate memory to read in file: %ls", wzSrcPath);
963 }
964
965 DWORD cbTotalRead = 0;
966 DWORD cbRead = 0;
967 do
968 {
969 DWORD cbRemaining = 0;
970 hr = ::ULongSub(cbData, cbTotalRead, &cbRemaining);
971 FileExitOnFailure(hr, "Underflow calculating remaining buffer size.");
972
973 if (!::ReadFile(hFile, pbData + cbTotalRead, cbRemaining, &cbRead, NULL))
974 {
975 FileExitWithLastError(hr, "Failed to read from file: %ls", wzSrcPath);
976 }
977
978 cbTotalRead += cbRead;
979 } while (cbRead);
980
981 if (cbTotalRead != cbData)
982 {
983 hr = E_UNEXPECTED;
984 FileExitOnFailure(hr, "Failed to completely read file: %ls", wzSrcPath);
985 }
986
987 *ppbDest = pbData;
988 pbData = NULL;
989 *pcbDest = cbData;
990
991LExit:
992 ReleaseMem(pbData);
993 ReleaseFile(hFile);
994
995 return hr;
996}
997
998extern "C" HRESULT DAPI FileReadHandle(
999 __in HANDLE hFile,
1000 __in_bcount(cbDest) LPBYTE pbDest,
1001 __in SIZE_T cbDest
1002 )
1003{
1004 HRESULT hr = 0;
1005 DWORD cbDataRead = 0;
1006 SIZE_T cbRemaining = cbDest;
1007 SIZE_T cbTotal = 0;
1008
1009 while (0 < cbRemaining)
1010 {
1011 if (!::ReadFile(hFile, pbDest + cbTotal, (DWORD)min(DWORD_MAX, cbRemaining), &cbDataRead, NULL))
1012 {
1013 DWORD er = ::GetLastError();
1014 if (ERROR_MORE_DATA == er)
1015 {
1016 hr = S_OK;
1017 }
1018 else
1019 {
1020 hr = HRESULT_FROM_WIN32(er);
1021 }
1022 FileExitOnRootFailure(hr, "Failed to read data from file handle.");
1023 }
1024
1025 cbRemaining -= cbDataRead;
1026 cbTotal += cbDataRead;
1027 }
1028
1029LExit:
1030 return hr;
1031}
1032
1033
1034/*******************************************************************
1035 FileWrite - write a file from memory
1036
1037********************************************************************/
1038extern "C" HRESULT DAPI FileWrite(
1039 __in_z LPCWSTR pwzFileName,
1040 __in DWORD dwFlagsAndAttributes,
1041 __in_bcount_opt(cbData) LPCBYTE pbData,
1042 __in SIZE_T cbData,
1043 __out_opt HANDLE* pHandle
1044 )
1045{
1046 HRESULT hr = S_OK;
1047 HANDLE hFile = INVALID_HANDLE_VALUE;
1048
1049 // Open the file
1050 hFile = ::CreateFileW(pwzFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, dwFlagsAndAttributes, NULL);
1051 FileExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open file: %ls", pwzFileName);
1052
1053 hr = FileWriteHandle(hFile, pbData, cbData);
1054 FileExitOnFailure(hr, "Failed to write to file: %ls", pwzFileName);
1055
1056 if (pHandle)
1057 {
1058 *pHandle = hFile;
1059 hFile = INVALID_HANDLE_VALUE;
1060 }
1061
1062LExit:
1063 ReleaseFile(hFile);
1064
1065 return hr;
1066}
1067
1068
1069/*******************************************************************
1070 FileWriteHandle - write to a file handle from memory
1071
1072********************************************************************/
1073extern "C" HRESULT DAPI FileWriteHandle(
1074 __in HANDLE hFile,
1075 __in_bcount_opt(cbData) LPCBYTE pbData,
1076 __in SIZE_T cbData
1077 )
1078{
1079 HRESULT hr = S_OK;
1080 DWORD cbDataWritten = 0;
1081 SIZE_T cbTotal = 0;
1082 SIZE_T cbRemaining = cbData;
1083
1084 // Write out all of the data.
1085 while (0 < cbRemaining)
1086 {
1087 if (!::WriteFile(hFile, pbData + cbTotal, (DWORD)min(DWORD_MAX, cbRemaining), &cbDataWritten, NULL))
1088 {
1089 FileExitOnLastError(hr, "Failed to write data to file handle.");
1090 }
1091
1092 cbRemaining -= cbDataWritten;
1093 cbTotal += cbDataWritten;
1094 }
1095
1096LExit:
1097 return hr;
1098}
1099
1100
1101/*******************************************************************
1102 FileCopyUsingHandles
1103
1104*******************************************************************/
1105extern "C" HRESULT DAPI FileCopyUsingHandles(
1106 __in HANDLE hSource,
1107 __in HANDLE hTarget,
1108 __in DWORD64 cbCopy,
1109 __out_opt DWORD64* pcbCopied
1110 )
1111{
1112 HRESULT hr = S_OK;
1113 DWORD64 cbTotalCopied = 0;
1114 BYTE rgbData[4 * 1024];
1115 DWORD cbRead = 0;
1116
1117 do
1118 {
1119 cbRead = static_cast<DWORD>((0 == cbCopy) ? countof(rgbData) : min(countof(rgbData), cbCopy - cbTotalCopied));
1120 if (!::ReadFile(hSource, rgbData, cbRead, &cbRead, NULL))
1121 {
1122 FileExitWithLastError(hr, "Failed to read from source.");
1123 }
1124
1125 if (cbRead)
1126 {
1127 hr = FileWriteHandle(hTarget, rgbData, cbRead);
1128 FileExitOnFailure(hr, "Failed to write to target.");
1129 }
1130
1131 cbTotalCopied += cbRead;
1132 } while (cbTotalCopied < cbCopy && 0 != cbRead);
1133
1134 if (pcbCopied)
1135 {
1136 *pcbCopied = cbTotalCopied;
1137 }
1138
1139LExit:
1140 return hr;
1141}
1142
1143
1144/*******************************************************************
1145 FileCopyUsingHandlesWithProgress
1146
1147*******************************************************************/
1148extern "C" HRESULT DAPI FileCopyUsingHandlesWithProgress(
1149 __in HANDLE hSource,
1150 __in HANDLE hTarget,
1151 __in DWORD64 cbCopy,
1152 __in_opt LPPROGRESS_ROUTINE lpProgressRoutine,
1153 __in_opt LPVOID lpData
1154 )
1155{
1156 HRESULT hr = S_OK;
1157 DWORD64 cbTotalCopied = 0;
1158 BYTE rgbData[64 * 1024];
1159 DWORD cbRead = 0;
1160
1161 LARGE_INTEGER liSourceSize = { };
1162 LARGE_INTEGER liTotalCopied = { };
1163 LARGE_INTEGER liZero = { };
1164 DWORD dwResult = 0;
1165
1166 hr = FileSizeByHandle(hSource, &liSourceSize.QuadPart);
1167 FileExitOnFailure(hr, "Failed to get size of source.");
1168
1169 if (0 < cbCopy && cbCopy < (DWORD64)liSourceSize.QuadPart)
1170 {
1171 liSourceSize.QuadPart = cbCopy;
1172 }
1173
1174 if (lpProgressRoutine)
1175 {
1176 dwResult = lpProgressRoutine(liSourceSize, liTotalCopied, liZero, liZero, 0, CALLBACK_STREAM_SWITCH, hSource, hTarget, lpData);
1177 switch (dwResult)
1178 {
1179 case PROGRESS_CONTINUE:
1180 break;
1181
1182 case PROGRESS_CANCEL:
1183 ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_REQUEST_ABORTED));
1184
1185 case PROGRESS_STOP:
1186 ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_REQUEST_ABORTED));
1187
1188 case PROGRESS_QUIET:
1189 lpProgressRoutine = NULL;
1190 break;
1191 }
1192 }
1193
1194 // Set size of the target file.
1195 ::SetFilePointerEx(hTarget, liSourceSize, NULL, FILE_BEGIN);
1196
1197 if (!::SetEndOfFile(hTarget))
1198 {
1199 FileExitWithLastError(hr, "Failed to set end of target file.");
1200 }
1201
1202 if (!::SetFilePointerEx(hTarget, liZero, NULL, FILE_BEGIN))
1203 {
1204 FileExitWithLastError(hr, "Failed to reset target file pointer.");
1205 }
1206
1207 // Copy with progress.
1208 while (0 == cbCopy || cbTotalCopied < cbCopy)
1209 {
1210 cbRead = static_cast<DWORD>((0 == cbCopy) ? countof(rgbData) : min(countof(rgbData), cbCopy - cbTotalCopied));
1211 if (!::ReadFile(hSource, rgbData, cbRead, &cbRead, NULL))
1212 {
1213 FileExitWithLastError(hr, "Failed to read from source.");
1214 }
1215
1216 if (cbRead)
1217 {
1218 hr = FileWriteHandle(hTarget, rgbData, cbRead);
1219 FileExitOnFailure(hr, "Failed to write to target.");
1220
1221 cbTotalCopied += cbRead;
1222
1223 if (lpProgressRoutine)
1224 {
1225 liTotalCopied.QuadPart = cbTotalCopied;
1226 dwResult = lpProgressRoutine(liSourceSize, liTotalCopied, liZero, liZero, 0, CALLBACK_CHUNK_FINISHED, hSource, hTarget, lpData);
1227 switch (dwResult)
1228 {
1229 case PROGRESS_CONTINUE:
1230 break;
1231
1232 case PROGRESS_CANCEL:
1233 ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_REQUEST_ABORTED));
1234
1235 case PROGRESS_STOP:
1236 ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_REQUEST_ABORTED));
1237
1238 case PROGRESS_QUIET:
1239 lpProgressRoutine = NULL;
1240 break;
1241 }
1242 }
1243 }
1244 else
1245 {
1246 break;
1247 }
1248 }
1249
1250LExit:
1251 return hr;
1252}
1253
1254
1255/*******************************************************************
1256 FileEnsureCopy
1257
1258*******************************************************************/
1259extern "C" HRESULT DAPI FileEnsureCopy(
1260 __in_z LPCWSTR wzSource,
1261 __in_z LPCWSTR wzTarget,
1262 __in BOOL fOverwrite
1263 )
1264{
1265 HRESULT hr = S_OK;
1266 DWORD er;
1267
1268 // try to copy the file first
1269 if (::CopyFileW(wzSource, wzTarget, !fOverwrite))
1270 {
1271 ExitFunction(); // we're done
1272 }
1273
1274 er = ::GetLastError(); // check the error and do the right thing below
1275 if (!fOverwrite && (ERROR_FILE_EXISTS == er || ERROR_ALREADY_EXISTS == er))
1276 {
1277 // if not overwriting this is an expected error
1278 ExitFunction1(hr = S_FALSE);
1279 }
1280 else if (ERROR_PATH_NOT_FOUND == er) // if the path doesn't exist
1281 {
1282 // try to create the directory then do the copy
1283 LPWSTR pwzLastSlash = NULL;
1284 for (LPWSTR pwz = const_cast<LPWSTR>(wzTarget); *pwz; ++pwz)
1285 {
1286 if (*pwz == L'\\')
1287 {
1288 pwzLastSlash = pwz;
1289 }
1290 }
1291
1292 if (pwzLastSlash)
1293 {
1294 *pwzLastSlash = L'\0'; // null terminate
1295 hr = DirEnsureExists(wzTarget, NULL);
1296 *pwzLastSlash = L'\\'; // now put the slash back
1297 FileExitOnFailureDebugTrace(hr, "failed to create directory while copying file: '%ls' to: '%ls'", wzSource, wzTarget);
1298
1299 // try to copy again
1300 if (!::CopyFileW(wzSource, wzTarget, fOverwrite))
1301 {
1302 FileExitOnLastErrorDebugTrace(hr, "failed to copy file: '%ls' to: '%ls'", wzSource, wzTarget);
1303 }
1304 }
1305 else // no path was specified so just return the error
1306 {
1307 hr = HRESULT_FROM_WIN32(er);
1308 }
1309 }
1310 else // unexpected error
1311 {
1312 hr = HRESULT_FROM_WIN32(er);
1313 }
1314
1315LExit:
1316 return hr;
1317}
1318
1319
1320/*******************************************************************
1321 FileEnsureCopyWithRetry
1322
1323*******************************************************************/
1324extern "C" HRESULT DAPI FileEnsureCopyWithRetry(
1325 __in LPCWSTR wzSource,
1326 __in LPCWSTR wzTarget,
1327 __in BOOL fOverwrite,
1328 __in DWORD cRetry,
1329 __in DWORD dwWaitMilliseconds
1330 )
1331{
1332 AssertSz(cRetry != DWORD_MAX, "Cannot pass DWORD_MAX for retry.");
1333
1334 HRESULT hr = E_FAIL;
1335 DWORD i = 0;
1336
1337 for (i = 0; FAILED(hr) && i <= cRetry; ++i)
1338 {
1339 if (0 < i)
1340 {
1341 ::Sleep(dwWaitMilliseconds);
1342 }
1343
1344 hr = FileEnsureCopy(wzSource, wzTarget, fOverwrite);
1345 if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr || HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr
1346 || HRESULT_FROM_WIN32(ERROR_FILE_EXISTS) == hr || HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS) == hr)
1347 {
1348 break; // no reason to retry these errors.
1349 }
1350 }
1351 FileExitOnFailure(hr, "Failed to copy file: '%ls' to: '%ls' after %u retries.", wzSource, wzTarget, i);
1352
1353LExit:
1354 return hr;
1355}
1356
1357
1358/*******************************************************************
1359 FileEnsureMove
1360
1361*******************************************************************/
1362extern "C" HRESULT DAPI FileEnsureMove(
1363 __in_z LPCWSTR wzSource,
1364 __in_z LPCWSTR wzTarget,
1365 __in BOOL fOverwrite,
1366 __in BOOL fAllowCopy
1367 )
1368{
1369 HRESULT hr = S_OK;
1370 DWORD er;
1371
1372 DWORD dwFlags = 0;
1373
1374 if (fOverwrite)
1375 {
1376 dwFlags |= MOVEFILE_REPLACE_EXISTING;
1377 }
1378 if (fAllowCopy)
1379 {
1380 dwFlags |= MOVEFILE_COPY_ALLOWED;
1381 }
1382
1383 // try to move the file first
1384 if (::MoveFileExW(wzSource, wzTarget, dwFlags))
1385 {
1386 ExitFunction(); // we're done
1387 }
1388
1389 er = ::GetLastError(); // check the error and do the right thing below
1390 if (!fOverwrite && (ERROR_FILE_EXISTS == er || ERROR_ALREADY_EXISTS == er))
1391 {
1392 // if not overwriting this is an expected error
1393 ExitFunction1(hr = S_FALSE);
1394 }
1395 else if (ERROR_FILE_NOT_FOUND == er)
1396 {
1397 // We are seeing some cases where ::MoveFileEx() says a file was not found
1398 // but the source file is actually present. In that case, return path not
1399 // found so we try to create the target path since that is most likely
1400 // what is missing. Otherwise, the source file is missing and we're obviously
1401 // not going to be recovering from that.
1402 if (FileExistsEx(wzSource, NULL))
1403 {
1404 er = ERROR_PATH_NOT_FOUND;
1405 }
1406 }
1407
1408 // If the path doesn't exist, try to create the directory tree then do the move.
1409 if (ERROR_PATH_NOT_FOUND == er)
1410 {
1411 LPWSTR pwzLastSlash = NULL;
1412 for (LPWSTR pwz = const_cast<LPWSTR>(wzTarget); *pwz; ++pwz)
1413 {
1414 if (*pwz == L'\\')
1415 {
1416 pwzLastSlash = pwz;
1417 }
1418 }
1419
1420 if (pwzLastSlash)
1421 {
1422 *pwzLastSlash = L'\0'; // null terminate
1423 hr = DirEnsureExists(wzTarget, NULL);
1424 *pwzLastSlash = L'\\'; // now put the slash back
1425 FileExitOnFailureDebugTrace(hr, "failed to create directory while moving file: '%ls' to: '%ls'", wzSource, wzTarget);
1426
1427 // try to move again
1428 if (!::MoveFileExW(wzSource, wzTarget, dwFlags))
1429 {
1430 FileExitOnLastErrorDebugTrace(hr, "failed to move file: '%ls' to: '%ls'", wzSource, wzTarget);
1431 }
1432 }
1433 else // no path was specified so just return the error
1434 {
1435 hr = HRESULT_FROM_WIN32(er);
1436 }
1437 }
1438 else // unexpected error
1439 {
1440 hr = HRESULT_FROM_WIN32(er);
1441 }
1442
1443LExit:
1444 return hr;
1445}
1446
1447
1448/*******************************************************************
1449 FileEnsureMoveWithRetry
1450
1451*******************************************************************/
1452extern "C" HRESULT DAPI FileEnsureMoveWithRetry(
1453 __in LPCWSTR wzSource,
1454 __in LPCWSTR wzTarget,
1455 __in BOOL fOverwrite,
1456 __in BOOL fAllowCopy,
1457 __in DWORD cRetry,
1458 __in DWORD dwWaitMilliseconds
1459 )
1460{
1461 AssertSz(cRetry != DWORD_MAX, "Cannot pass DWORD_MAX for retry.");
1462
1463 HRESULT hr = E_FAIL;
1464 DWORD i = 0;
1465
1466 for (i = 0; FAILED(hr) && i < cRetry + 1; ++i)
1467 {
1468 if (0 < i)
1469 {
1470 ::Sleep(dwWaitMilliseconds);
1471 }
1472
1473 hr = FileEnsureMove(wzSource, wzTarget, fOverwrite, fAllowCopy);
1474 }
1475 FileExitOnFailure(hr, "Failed to move file: '%ls' to: '%ls' after %u retries.", wzSource, wzTarget, i);
1476
1477LExit:
1478 return hr;
1479}
1480
1481
1482/*******************************************************************
1483 FileCreateTemp - creates an empty temp file
1484
1485 NOTE: uses ANSI functions internally so it is Win9x safe
1486********************************************************************/
1487extern "C" HRESULT DAPI FileCreateTemp(
1488 __in_z LPCWSTR wzPrefix,
1489 __in_z LPCWSTR wzExtension,
1490 __deref_opt_out_z LPWSTR* ppwzTempFile,
1491 __out_opt HANDLE* phTempFile
1492 )
1493{
1494 Assert(wzPrefix && *wzPrefix);
1495 HRESULT hr = S_OK;
1496 LPSTR pszTempPath = NULL;
1497 DWORD cchTempPath = MAX_PATH;
1498
1499 HANDLE hTempFile = INVALID_HANDLE_VALUE;
1500 LPSTR pszTempFile = NULL;
1501
1502 int i = 0;
1503
1504 hr = StrAnsiAlloc(&pszTempPath, cchTempPath);
1505 FileExitOnFailure(hr, "failed to allocate memory for the temp path");
1506 ::GetTempPathA(cchTempPath, pszTempPath);
1507
1508 for (i = 0; i < 1000 && INVALID_HANDLE_VALUE == hTempFile; ++i)
1509 {
1510 hr = StrAnsiAllocFormatted(&pszTempFile, "%s%ls%05d.%ls", pszTempPath, wzPrefix, i, wzExtension);
1511 FileExitOnFailure(hr, "failed to allocate memory for log file");
1512
1513 hTempFile = ::CreateFileA(pszTempFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
1514 if (INVALID_HANDLE_VALUE == hTempFile)
1515 {
1516 // if the file already exists, just try again
1517 hr = HRESULT_FROM_WIN32(::GetLastError());
1518 if (HRESULT_FROM_WIN32(ERROR_FILE_EXISTS) == hr)
1519 {
1520 hr = S_OK;
1521 continue;
1522 }
1523 FileExitOnFailureDebugTrace(hr, "failed to create file: %hs", pszTempFile);
1524 }
1525 }
1526
1527 if (ppwzTempFile)
1528 {
1529 hr = StrAllocStringAnsi(ppwzTempFile, pszTempFile, 0, CP_UTF8);
1530 }
1531
1532 if (phTempFile)
1533 {
1534 *phTempFile = hTempFile;
1535 hTempFile = INVALID_HANDLE_VALUE;
1536 }
1537
1538LExit:
1539 ReleaseFile(hTempFile);
1540 ReleaseStr(pszTempFile);
1541 ReleaseStr(pszTempPath);
1542
1543 return hr;
1544}
1545
1546
1547/*******************************************************************
1548 FileCreateTempW - creates an empty temp file
1549
1550*******************************************************************/
1551extern "C" HRESULT DAPI FileCreateTempW(
1552 __in_z LPCWSTR wzPrefix,
1553 __in_z LPCWSTR wzExtension,
1554 __deref_opt_out_z LPWSTR* ppwzTempFile,
1555 __out_opt HANDLE* phTempFile
1556 )
1557{
1558 Assert(wzPrefix && *wzPrefix);
1559 HRESULT hr = E_FAIL;
1560
1561 WCHAR wzTempPath[MAX_PATH];
1562 DWORD cchTempPath = countof(wzTempPath);
1563 LPWSTR pwzTempFile = NULL;
1564
1565 HANDLE hTempFile = INVALID_HANDLE_VALUE;
1566 int i = 0;
1567
1568 if (!::GetTempPathW(cchTempPath, wzTempPath))
1569 {
1570 FileExitOnLastError(hr, "failed to get temp path");
1571 }
1572
1573 for (i = 0; i < 1000 && INVALID_HANDLE_VALUE == hTempFile; ++i)
1574 {
1575 hr = StrAllocFormatted(&pwzTempFile, L"%s%s%05d.%s", wzTempPath, wzPrefix, i, wzExtension);
1576 FileExitOnFailure(hr, "failed to allocate memory for temp filename");
1577
1578 hTempFile = ::CreateFileW(pwzTempFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
1579 if (INVALID_HANDLE_VALUE == hTempFile)
1580 {
1581 // if the file already exists, just try again
1582 hr = HRESULT_FROM_WIN32(::GetLastError());
1583 if (HRESULT_FROM_WIN32(ERROR_FILE_EXISTS) == hr)
1584 {
1585 hr = S_OK;
1586 continue;
1587 }
1588 FileExitOnFailureDebugTrace(hr, "failed to create file: %ls", pwzTempFile);
1589 }
1590 }
1591
1592 if (phTempFile)
1593 {
1594 *phTempFile = hTempFile;
1595 hTempFile = INVALID_HANDLE_VALUE;
1596 }
1597
1598 if (ppwzTempFile)
1599 {
1600 *ppwzTempFile = pwzTempFile;
1601 pwzTempFile = NULL;
1602 }
1603
1604LExit:
1605 ReleaseFile(hTempFile);
1606 ReleaseStr(pwzTempFile);
1607
1608 return hr;
1609}
1610
1611
1612/*******************************************************************
1613 FileIsSame
1614
1615********************************************************************/
1616extern "C" HRESULT DAPI FileIsSame(
1617 __in_z LPCWSTR wzFile1,
1618 __in_z LPCWSTR wzFile2,
1619 __out LPBOOL lpfSameFile
1620 )
1621{
1622 HRESULT hr = S_OK;
1623 HANDLE hFile1 = NULL;
1624 HANDLE hFile2 = NULL;
1625 BY_HANDLE_FILE_INFORMATION fileInfo1 = { };
1626 BY_HANDLE_FILE_INFORMATION fileInfo2 = { };
1627
1628 hFile1 = ::CreateFileW(wzFile1, FILE_READ_ATTRIBUTES, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
1629 FileExitOnInvalidHandleWithLastError(hFile1, hr, "Failed to open file 1. File = '%ls'", wzFile1);
1630
1631 hFile2 = ::CreateFileW(wzFile2, FILE_READ_ATTRIBUTES, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
1632 FileExitOnInvalidHandleWithLastError(hFile2, hr, "Failed to open file 2. File = '%ls'", wzFile2);
1633
1634 if (!::GetFileInformationByHandle(hFile1, &fileInfo1))
1635 {
1636 FileExitWithLastError(hr, "Failed to get information for file 1. File = '%ls'", wzFile1);
1637 }
1638
1639 if (!::GetFileInformationByHandle(hFile2, &fileInfo2))
1640 {
1641 FileExitWithLastError(hr, "Failed to get information for file 2. File = '%ls'", wzFile2);
1642 }
1643
1644 *lpfSameFile = fileInfo1.dwVolumeSerialNumber == fileInfo2.dwVolumeSerialNumber &&
1645 fileInfo1.nFileIndexHigh == fileInfo2.nFileIndexHigh &&
1646 fileInfo1.nFileIndexLow == fileInfo2.nFileIndexLow ? TRUE : FALSE;
1647
1648LExit:
1649 ReleaseFile(hFile1);
1650 ReleaseFile(hFile2);
1651
1652 return hr;
1653}
1654
1655/*******************************************************************
1656 FileEnsureDelete - deletes a file, first removing read-only,
1657 hidden, or system attributes if necessary.
1658********************************************************************/
1659extern "C" HRESULT DAPI FileEnsureDelete(
1660 __in_z LPCWSTR wzFile
1661 )
1662{
1663 HRESULT hr = S_OK;
1664
1665 DWORD dwAttrib = INVALID_FILE_ATTRIBUTES;
1666 if (FileExistsEx(wzFile, &dwAttrib))
1667 {
1668 if (dwAttrib & FILE_ATTRIBUTE_READONLY || dwAttrib & FILE_ATTRIBUTE_HIDDEN || dwAttrib & FILE_ATTRIBUTE_SYSTEM)
1669 {
1670 if (!::SetFileAttributesW(wzFile, FILE_ATTRIBUTE_NORMAL))
1671 {
1672 FileExitOnLastError(hr, "Failed to remove attributes from file: %ls", wzFile);
1673 }
1674 }
1675
1676 if (!::DeleteFileW(wzFile))
1677 {
1678 FileExitOnLastError(hr, "Failed to delete file: %ls", wzFile);
1679 }
1680 }
1681
1682LExit:
1683 return hr;
1684}
1685
1686/*******************************************************************
1687 FileGetTime - Gets the file time of a specified file
1688********************************************************************/
1689extern "C" HRESULT DAPI FileGetTime(
1690 __in_z LPCWSTR wzFile,
1691 __out_opt LPFILETIME lpCreationTime,
1692 __out_opt LPFILETIME lpLastAccessTime,
1693 __out_opt LPFILETIME lpLastWriteTime
1694 )
1695{
1696 HRESULT hr = S_OK;
1697 HANDLE hFile = NULL;
1698
1699 hFile = ::CreateFileW(wzFile, FILE_READ_ATTRIBUTES, FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL);
1700 FileExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open file. File = '%ls'", wzFile);
1701
1702 if (!::GetFileTime(hFile, lpCreationTime, lpLastAccessTime, lpLastWriteTime))
1703 {
1704 FileExitWithLastError(hr, "Failed to get file time for file. File = '%ls'", wzFile);
1705 }
1706
1707LExit:
1708 ReleaseFile(hFile);
1709 return hr;
1710}
1711
1712/*******************************************************************
1713 FileSetTime - Sets the file time of a specified file
1714********************************************************************/
1715extern "C" HRESULT DAPI FileSetTime(
1716 __in_z LPCWSTR wzFile,
1717 __in_opt const FILETIME *lpCreationTime,
1718 __in_opt const FILETIME *lpLastAccessTime,
1719 __in_opt const FILETIME *lpLastWriteTime
1720 )
1721{
1722 HRESULT hr = S_OK;
1723 HANDLE hFile = NULL;
1724
1725 hFile = ::CreateFileW(wzFile, FILE_WRITE_ATTRIBUTES, FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL);
1726 FileExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open file. File = '%ls'", wzFile);
1727
1728 if (!::SetFileTime(hFile, lpCreationTime, lpLastAccessTime, lpLastWriteTime))
1729 {
1730 FileExitWithLastError(hr, "Failed to set file time for file. File = '%ls'", wzFile);
1731 }
1732
1733LExit:
1734 ReleaseFile(hFile);
1735 return hr;
1736}
1737
1738/*******************************************************************
1739 FileReSetTime - ReSets a file's last acess and modified time to the
1740 creation time of the file
1741********************************************************************/
1742extern "C" HRESULT DAPI FileResetTime(
1743 __in_z LPCWSTR wzFile
1744 )
1745{
1746 HRESULT hr = S_OK;
1747 HANDLE hFile = NULL;
1748 FILETIME ftCreateTime;
1749
1750 hFile = ::CreateFileW(wzFile, FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
1751 FileExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open file. File = '%ls'", wzFile);
1752
1753 if (!::GetFileTime(hFile, &ftCreateTime, NULL, NULL))
1754 {
1755 FileExitWithLastError(hr, "Failed to get file time for file. File = '%ls'", wzFile);
1756 }
1757
1758 if (!::SetFileTime(hFile, NULL, NULL, &ftCreateTime))
1759 {
1760 FileExitWithLastError(hr, "Failed to reset file time for file. File = '%ls'", wzFile);
1761 }
1762
1763LExit:
1764 ReleaseFile(hFile);
1765 return hr;
1766}
1767
1768
1769/*******************************************************************
1770 FileExecutableArchitecture
1771
1772*******************************************************************/
1773extern "C" HRESULT DAPI FileExecutableArchitecture(
1774 __in_z LPCWSTR wzFile,
1775 __out FILE_ARCHITECTURE *pArchitecture
1776 )
1777{
1778 HRESULT hr = S_OK;
1779
1780 HANDLE hFile = INVALID_HANDLE_VALUE;
1781 DWORD cbRead = 0;
1782 IMAGE_DOS_HEADER DosImageHeader = { };
1783 IMAGE_NT_HEADERS NtImageHeader = { };
1784
1785 hFile = ::CreateFileW(wzFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
1786 if (hFile == INVALID_HANDLE_VALUE)
1787 {
1788 FileExitWithLastError(hr, "Failed to open file: %ls", wzFile);
1789 }
1790
1791 if (!::ReadFile(hFile, &DosImageHeader, sizeof(DosImageHeader), &cbRead, NULL))
1792 {
1793 FileExitWithLastError(hr, "Failed to read DOS header from file: %ls", wzFile);
1794 }
1795
1796 if (DosImageHeader.e_magic != IMAGE_DOS_SIGNATURE)
1797 {
1798 hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT);
1799 FileExitOnRootFailure(hr, "Read invalid DOS header from file: %ls", wzFile);
1800 }
1801
1802 if (INVALID_SET_FILE_POINTER == ::SetFilePointer(hFile, DosImageHeader.e_lfanew, NULL, FILE_BEGIN))
1803 {
1804 FileExitWithLastError(hr, "Failed to seek the NT header in file: %ls", wzFile);
1805 }
1806
1807 if (!::ReadFile(hFile, &NtImageHeader, sizeof(NtImageHeader), &cbRead, NULL))
1808 {
1809 FileExitWithLastError(hr, "Failed to read NT header from file: %ls", wzFile);
1810 }
1811
1812 if (NtImageHeader.Signature != IMAGE_NT_SIGNATURE)
1813 {
1814 hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT);
1815 FileExitOnRootFailure(hr, "Read invalid NT header from file: %ls", wzFile);
1816 }
1817
1818 if (IMAGE_SUBSYSTEM_NATIVE == NtImageHeader.OptionalHeader.Subsystem ||
1819 IMAGE_SUBSYSTEM_WINDOWS_GUI == NtImageHeader.OptionalHeader.Subsystem ||
1820 IMAGE_SUBSYSTEM_WINDOWS_CUI == NtImageHeader.OptionalHeader.Subsystem)
1821 {
1822 switch (NtImageHeader.FileHeader.Machine)
1823 {
1824 case IMAGE_FILE_MACHINE_I386:
1825 *pArchitecture = FILE_ARCHITECTURE_X86;
1826 break;
1827 case IMAGE_FILE_MACHINE_IA64:
1828 *pArchitecture = FILE_ARCHITECTURE_IA64;
1829 break;
1830 case IMAGE_FILE_MACHINE_AMD64:
1831 *pArchitecture = FILE_ARCHITECTURE_X64;
1832 break;
1833 default:
1834 hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT);
1835 break;
1836 }
1837 }
1838 else
1839 {
1840 hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT);
1841 }
1842 FileExitOnFailure(hr, "Unexpected subsystem: %d machine type: %d specified in NT header from file: %ls", NtImageHeader.OptionalHeader.Subsystem, NtImageHeader.FileHeader.Machine, wzFile);
1843
1844LExit:
1845 if (hFile != INVALID_HANDLE_VALUE)
1846 {
1847 ::CloseHandle(hFile);
1848 }
1849
1850 return hr;
1851}
1852
1853/*******************************************************************
1854 FileToString
1855
1856*******************************************************************/
1857extern "C" HRESULT DAPI FileToString(
1858 __in_z LPCWSTR wzFile,
1859 __out LPWSTR *psczString,
1860 __out_opt FILE_ENCODING *pfeEncoding
1861 )
1862{
1863 HRESULT hr = S_OK;
1864 BYTE *pbFullFileBuffer = NULL;
1865 SIZE_T cbFullFileBuffer = 0;
1866 BOOL fNullCharFound = FALSE;
1867 LPWSTR sczFileText = NULL;
1868
1869 // Check if the file is ANSI
1870 hr = FileRead(&pbFullFileBuffer, &cbFullFileBuffer, wzFile);
1871 FileExitOnFailure(hr, "Failed to read file: %ls", wzFile);
1872
1873 if (0 == cbFullFileBuffer)
1874 {
1875 *psczString = NULL;
1876 ExitFunction1(hr = S_OK);
1877 }
1878
1879 // UTF-8 BOM
1880 if (cbFullFileBuffer > sizeof(UTF8BOM) && 0 == memcmp(pbFullFileBuffer, UTF8BOM, sizeof(UTF8BOM)))
1881 {
1882 if (pfeEncoding)
1883 {
1884 *pfeEncoding = FILE_ENCODING_UTF8_WITH_BOM;
1885 }
1886
1887 hr = StrAllocStringAnsi(&sczFileText, reinterpret_cast<LPCSTR>(pbFullFileBuffer + 3), cbFullFileBuffer - 3, CP_UTF8);
1888 FileExitOnFailure(hr, "Failed to convert file %ls from UTF-8 as its BOM indicated", wzFile);
1889
1890 *psczString = sczFileText;
1891 sczFileText = NULL;
1892 }
1893 // UTF-16 BOM, little endian (windows regular UTF-16)
1894 else if (cbFullFileBuffer > sizeof(UTF16BOM) && 0 == memcmp(pbFullFileBuffer, UTF16BOM, sizeof(UTF16BOM)))
1895 {
1896 if (pfeEncoding)
1897 {
1898 *pfeEncoding = FILE_ENCODING_UTF16_WITH_BOM;
1899 }
1900
1901 hr = StrAllocString(psczString, reinterpret_cast<LPWSTR>(pbFullFileBuffer + 2), (cbFullFileBuffer - 2) / sizeof(WCHAR));
1902 FileExitOnFailure(hr, "Failed to allocate copy of string");
1903 }
1904 // No BOM, let's try to detect
1905 else
1906 {
1907 for (DWORD i = 0; i < cbFullFileBuffer; ++i)
1908 {
1909 if (pbFullFileBuffer[i] == '\0')
1910 {
1911 fNullCharFound = TRUE;
1912 break;
1913 }
1914 }
1915
1916 if (!fNullCharFound)
1917 {
1918 if (pfeEncoding)
1919 {
1920 *pfeEncoding = FILE_ENCODING_UTF8;
1921 }
1922
1923 hr = StrAllocStringAnsi(&sczFileText, reinterpret_cast<LPCSTR>(pbFullFileBuffer), cbFullFileBuffer, CP_UTF8);
1924 if (FAILED(hr))
1925 {
1926 if (E_OUTOFMEMORY == hr)
1927 {
1928 FileExitOnFailure(hr, "Failed to convert file %ls from UTF-8", wzFile);
1929 }
1930 }
1931 else
1932 {
1933 *psczString = sczFileText;
1934 sczFileText = NULL;
1935 }
1936 }
1937 else if (NULL == *psczString)
1938 {
1939 if (pfeEncoding)
1940 {
1941 *pfeEncoding = FILE_ENCODING_UTF16;
1942 }
1943
1944 hr = StrAllocString(psczString, reinterpret_cast<LPWSTR>(pbFullFileBuffer), cbFullFileBuffer / sizeof(WCHAR));
1945 FileExitOnFailure(hr, "Failed to allocate copy of string");
1946 }
1947 }
1948
1949LExit:
1950 ReleaseStr(sczFileText);
1951 ReleaseMem(pbFullFileBuffer);
1952
1953 return hr;
1954}
1955
1956/*******************************************************************
1957 FileFromString
1958
1959*******************************************************************/
1960extern "C" HRESULT DAPI FileFromString(
1961 __in_z LPCWSTR wzFile,
1962 __in DWORD dwFlagsAndAttributes,
1963 __in_z LPCWSTR sczString,
1964 __in FILE_ENCODING feEncoding
1965 )
1966{
1967 HRESULT hr = S_OK;
1968 LPSTR sczUtf8String = NULL;
1969 BYTE *pbFullFileBuffer = NULL;
1970 const BYTE *pcbFullFileBuffer = NULL;
1971 SIZE_T cbFullFileBuffer = 0;
1972 SIZE_T cbStrLen = 0;
1973
1974 switch (feEncoding)
1975 {
1976 case FILE_ENCODING_UTF8:
1977 hr = StrAnsiAllocString(&sczUtf8String, sczString, 0, CP_UTF8);
1978 FileExitOnFailure(hr, "Failed to convert string to UTF-8 to write UTF-8 file");
1979
1980 hr = ::StringCchLengthA(sczUtf8String, STRSAFE_MAX_CCH, reinterpret_cast<size_t*>(&cbFullFileBuffer));
1981 FileExitOnRootFailure(hr, "Failed to get length of UTF-8 string");
1982
1983 pcbFullFileBuffer = reinterpret_cast<BYTE *>(sczUtf8String);
1984 break;
1985 case FILE_ENCODING_UTF8_WITH_BOM:
1986 hr = StrAnsiAllocString(&sczUtf8String, sczString, 0, CP_UTF8);
1987 FileExitOnFailure(hr, "Failed to convert string to UTF-8 to write UTF-8 file");
1988
1989 hr = ::StringCchLengthA(sczUtf8String, STRSAFE_MAX_CCH, reinterpret_cast<size_t*>(&cbStrLen));
1990 FileExitOnRootFailure(hr, "Failed to get length of UTF-8 string");
1991
1992 cbFullFileBuffer = sizeof(UTF8BOM) + cbStrLen;
1993
1994 pbFullFileBuffer = reinterpret_cast<BYTE *>(MemAlloc(cbFullFileBuffer, TRUE));
1995 FileExitOnNull(pbFullFileBuffer, hr, E_OUTOFMEMORY, "Failed to allocate memory for output file buffer");
1996
1997 memcpy_s(pbFullFileBuffer, sizeof(UTF8BOM), UTF8BOM, sizeof(UTF8BOM));
1998 memcpy_s(pbFullFileBuffer + sizeof(UTF8BOM), cbStrLen, sczUtf8String, cbStrLen);
1999 pcbFullFileBuffer = pbFullFileBuffer;
2000 break;
2001 case FILE_ENCODING_UTF16:
2002 hr = ::StringCchLengthW(sczString, STRSAFE_MAX_CCH, reinterpret_cast<size_t*>(&cbStrLen));
2003 FileExitOnRootFailure(hr, "Failed to get length of string");
2004
2005 cbFullFileBuffer = cbStrLen * sizeof(WCHAR);
2006 pcbFullFileBuffer = reinterpret_cast<const BYTE *>(sczString);
2007 break;
2008 case FILE_ENCODING_UTF16_WITH_BOM:
2009 hr = ::StringCchLengthW(sczString, STRSAFE_MAX_CCH, reinterpret_cast<size_t*>(&cbStrLen));
2010 FileExitOnRootFailure(hr, "Failed to get length of string");
2011
2012 cbStrLen *= sizeof(WCHAR);
2013 cbFullFileBuffer = sizeof(UTF16BOM) + cbStrLen;
2014
2015 pbFullFileBuffer = reinterpret_cast<BYTE *>(MemAlloc(cbFullFileBuffer, TRUE));
2016 FileExitOnNull(pbFullFileBuffer, hr, E_OUTOFMEMORY, "Failed to allocate memory for output file buffer");
2017
2018 memcpy_s(pbFullFileBuffer, sizeof(UTF16BOM), UTF16BOM, sizeof(UTF16BOM));
2019 memcpy_s(pbFullFileBuffer + sizeof(UTF16BOM), cbStrLen, sczString, cbStrLen);
2020 pcbFullFileBuffer = pbFullFileBuffer;
2021 break;
2022 }
2023
2024 hr = FileWrite(wzFile, dwFlagsAndAttributes, pcbFullFileBuffer, cbFullFileBuffer, NULL);
2025 FileExitOnFailure(hr, "Failed to write file from string to: %ls", wzFile);
2026
2027LExit:
2028 ReleaseStr(sczUtf8String);
2029 ReleaseMem(pbFullFileBuffer);
2030
2031 return hr;
2032}
diff --git a/src/libs/dutil/WixToolset.DUtil/gdiputil.cpp b/src/libs/dutil/WixToolset.DUtil/gdiputil.cpp
new file mode 100644
index 00000000..b5a0087c
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/gdiputil.cpp
@@ -0,0 +1,227 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5using namespace Gdiplus;
6
7
8// Exit macros
9#define GdipExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_GDIPUTIL, x, s, __VA_ARGS__)
10#define GdipExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_GDIPUTIL, x, s, __VA_ARGS__)
11#define GdipExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_GDIPUTIL, x, s, __VA_ARGS__)
12#define GdipExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_GDIPUTIL, x, s, __VA_ARGS__)
13#define GdipExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_GDIPUTIL, x, s, __VA_ARGS__)
14#define GdipExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_GDIPUTIL, x, s, __VA_ARGS__)
15#define GdipExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_GDIPUTIL, p, x, e, s, __VA_ARGS__)
16#define GdipExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_GDIPUTIL, p, x, s, __VA_ARGS__)
17#define GdipExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_GDIPUTIL, p, x, e, s, __VA_ARGS__)
18#define GdipExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_GDIPUTIL, p, x, s, __VA_ARGS__)
19#define GdipExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_GDIPUTIL, e, x, s, __VA_ARGS__)
20#define GdipExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_GDIPUTIL, g, x, s, __VA_ARGS__)
21
22/********************************************************************
23 GdipInitialize - initializes GDI+.
24
25 Note: pOutput must be non-NULL if pInput->SuppressBackgroundThread
26 is TRUE. See GdiplusStartup() for more information.
27********************************************************************/
28extern "C" HRESULT DAPI GdipInitialize(
29 __in const Gdiplus::GdiplusStartupInput* pInput,
30 __out ULONG_PTR* pToken,
31 __out_opt Gdiplus::GdiplusStartupOutput *pOutput
32 )
33{
34 AssertSz(!pInput->SuppressBackgroundThread || pOutput, "pOutput required if background thread suppressed.");
35
36 HRESULT hr = S_OK;
37 Status status = Ok;
38
39 status = GdiplusStartup(pToken, pInput, pOutput);
40 GdipExitOnGdipFailure(status, hr, "Failed to initialize GDI+.");
41
42LExit:
43 return hr;
44}
45
46/********************************************************************
47 GdipUninitialize - uninitializes GDI+.
48
49********************************************************************/
50extern "C" void DAPI GdipUninitialize(
51 __in ULONG_PTR token
52 )
53{
54 GdiplusShutdown(token);
55}
56
57/********************************************************************
58 GdipBitmapFromResource - read a GDI+ image out of a resource stream
59
60********************************************************************/
61extern "C" HRESULT DAPI GdipBitmapFromResource(
62 __in_opt HINSTANCE hinst,
63 __in_z LPCSTR szId,
64 __out Bitmap **ppBitmap
65 )
66{
67 HRESULT hr = S_OK;
68 LPVOID pvData = NULL;
69 DWORD cbData = 0;
70 HGLOBAL hGlobal = NULL;;
71 LPVOID pv = NULL;
72 IStream *pStream = NULL;
73 Bitmap *pBitmap = NULL;
74 Status gs = Ok;
75
76 hr = ResReadData(hinst, szId, &pvData, &cbData);
77 GdipExitOnFailure(hr, "Failed to load GDI+ bitmap from resource.");
78
79 // Have to copy the fixed resource data into moveable (heap) memory
80 // since that's what GDI+ expects.
81 hGlobal = ::GlobalAlloc(GMEM_MOVEABLE, cbData);
82 GdipExitOnNullWithLastError(hGlobal, hr, "Failed to allocate global memory.");
83
84 pv = ::GlobalLock(hGlobal);
85 GdipExitOnNullWithLastError(pv, hr, "Failed to lock global memory.");
86
87 memcpy(pv, pvData, cbData);
88
89 ::GlobalUnlock(pv); // no point taking any more memory than we have already
90 pv = NULL;
91
92 hr = ::CreateStreamOnHGlobal(hGlobal, TRUE, &pStream);
93 GdipExitOnFailure(hr, "Failed to allocate stream from global memory.");
94
95 hGlobal = NULL; // we gave the global memory to the stream object so it will close it
96
97 pBitmap = Bitmap::FromStream(pStream);
98 GdipExitOnNull(pBitmap, hr, E_OUTOFMEMORY, "Failed to allocate bitmap from stream.");
99
100 gs = pBitmap->GetLastStatus();
101 GdipExitOnGdipFailure(gs, hr, "Failed to load bitmap from stream.");
102
103 *ppBitmap = pBitmap;
104 pBitmap = NULL;
105
106LExit:
107 if (pBitmap)
108 {
109 delete pBitmap;
110 }
111
112 ReleaseObject(pStream);
113
114 if (pv)
115 {
116 ::GlobalUnlock(pv);
117 }
118
119 if (hGlobal)
120 {
121 ::GlobalFree(hGlobal);
122 }
123
124 return hr;
125}
126
127
128/********************************************************************
129 GdipBitmapFromFile - read a GDI+ image from a file.
130
131********************************************************************/
132extern "C" HRESULT DAPI GdipBitmapFromFile(
133 __in_z LPCWSTR wzFileName,
134 __out Bitmap **ppBitmap
135 )
136{
137 HRESULT hr = S_OK;
138 Bitmap *pBitmap = NULL;
139 Status gs = Ok;
140
141 GdipExitOnNull(ppBitmap, hr, E_INVALIDARG, "Invalid null wzFileName");
142
143 pBitmap = Bitmap::FromFile(wzFileName);
144 GdipExitOnNull(pBitmap, hr, E_OUTOFMEMORY, "Failed to allocate bitmap from file.");
145
146 gs = pBitmap->GetLastStatus();
147 GdipExitOnGdipFailure(gs, hr, "Failed to load bitmap from file: %ls", wzFileName);
148
149 *ppBitmap = pBitmap;
150 pBitmap = NULL;
151
152LExit:
153 if (pBitmap)
154 {
155 delete pBitmap;
156 }
157
158 return hr;
159}
160
161
162HRESULT DAPI GdipHresultFromStatus(
163 __in Gdiplus::Status gs
164 )
165{
166 switch (gs)
167 {
168 case Ok:
169 return S_OK;
170
171 case GenericError:
172 return E_FAIL;
173
174 case InvalidParameter:
175 return E_INVALIDARG;
176
177 case OutOfMemory:
178 return E_OUTOFMEMORY;
179
180 case ObjectBusy:
181 return HRESULT_FROM_WIN32(ERROR_BUSY);
182
183 case InsufficientBuffer:
184 return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
185
186 case NotImplemented:
187 return E_NOTIMPL;
188
189 case Win32Error:
190 return E_FAIL;
191
192 case WrongState:
193 return E_FAIL;
194
195 case Aborted:
196 return E_ABORT;
197
198 case FileNotFound:
199 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
200
201 case ValueOverflow:
202 return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
203
204 case AccessDenied:
205 return E_ACCESSDENIED;
206
207 case UnknownImageFormat:
208 return HRESULT_FROM_WIN32(ERROR_BAD_FORMAT);
209
210 case FontFamilyNotFound: __fallthrough;
211 case FontStyleNotFound: __fallthrough;
212 case NotTrueTypeFont:
213 return E_UNEXPECTED;
214
215 case UnsupportedGdiplusVersion:
216 return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
217
218 case GdiplusNotInitialized:
219 return E_UNEXPECTED;
220
221 case PropertyNotFound: __fallthrough;
222 case PropertyNotSupported:
223 return E_FAIL;
224 }
225
226 return E_UNEXPECTED;
227}
diff --git a/src/libs/dutil/WixToolset.DUtil/guidutil.cpp b/src/libs/dutil/WixToolset.DUtil/guidutil.cpp
new file mode 100644
index 00000000..204c9af2
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/guidutil.cpp
@@ -0,0 +1,54 @@
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// Exit macros
7#define GuidExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_GUIDUTIL, x, s, __VA_ARGS__)
8#define GuidExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_GUIDUTIL, x, s, __VA_ARGS__)
9#define GuidExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_GUIDUTIL, x, s, __VA_ARGS__)
10#define GuidExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_GUIDUTIL, x, s, __VA_ARGS__)
11#define GuidExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_GUIDUTIL, x, s, __VA_ARGS__)
12#define GuidExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_GUIDUTIL, x, s, __VA_ARGS__)
13#define GuidExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_GUIDUTIL, p, x, e, s, __VA_ARGS__)
14#define GuidExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_GUIDUTIL, p, x, s, __VA_ARGS__)
15#define GuidExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_GUIDUTIL, p, x, e, s, __VA_ARGS__)
16#define GuidExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_GUIDUTIL, p, x, s, __VA_ARGS__)
17#define GuidExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_GUIDUTIL, e, x, s, __VA_ARGS__)
18#define GuidExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_GUIDUTIL, g, x, s, __VA_ARGS__)
19
20extern "C" HRESULT DAPI GuidFixedCreate(
21 _Out_z_cap_c_(GUID_STRING_LENGTH) WCHAR* wzGuid
22 )
23{
24 HRESULT hr = S_OK;
25 UUID guid = { };
26
27 hr = HRESULT_FROM_RPC(::UuidCreate(&guid));
28 GuidExitOnFailure(hr, "UuidCreate failed.");
29
30 if (!::StringFromGUID2(guid, wzGuid, GUID_STRING_LENGTH))
31 {
32 hr = E_OUTOFMEMORY;
33 GuidExitOnRootFailure(hr, "Failed to convert guid into string.");
34 }
35
36LExit:
37 return hr;
38}
39
40extern "C" HRESULT DAPI GuidCreate(
41 __deref_out_z LPWSTR* psczGuid
42 )
43{
44 HRESULT hr = S_OK;
45
46 hr = StrAlloc(psczGuid, GUID_STRING_LENGTH);
47 GuidExitOnFailure(hr, "Failed to allocate space for guid");
48
49 hr = GuidFixedCreate(*psczGuid);
50 GuidExitOnFailure(hr, "Failed to create new guid.");
51
52LExit:
53 return hr;
54}
diff --git a/src/libs/dutil/WixToolset.DUtil/iis7util.cpp b/src/libs/dutil/WixToolset.DUtil/iis7util.cpp
new file mode 100644
index 00000000..d0a0b000
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/iis7util.cpp
@@ -0,0 +1,535 @@
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 "iis7util.h"
5
6
7// Exit macros
8#define IisExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_IIS7UTIL, x, s, __VA_ARGS__)
9#define IisExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_IIS7UTIL, x, s, __VA_ARGS__)
10#define IisExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_IIS7UTIL, x, s, __VA_ARGS__)
11#define IisExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_IIS7UTIL, x, s, __VA_ARGS__)
12#define IisExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_IIS7UTIL, x, s, __VA_ARGS__)
13#define IisExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_IIS7UTIL, x, s, __VA_ARGS__)
14#define IisExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_IIS7UTIL, p, x, e, s, __VA_ARGS__)
15#define IisExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_IIS7UTIL, p, x, s, __VA_ARGS__)
16#define IisExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_IIS7UTIL, p, x, e, s, __VA_ARGS__)
17#define IisExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_IIS7UTIL, p, x, s, __VA_ARGS__)
18#define IisExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_IIS7UTIL, e, x, s, __VA_ARGS__)
19#define IisExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_IIS7UTIL, g, x, s, __VA_ARGS__)
20
21#define ISSTRINGVARIANT(vt) (VT_BSTR == vt || VT_LPWSTR == vt)
22
23extern "C" HRESULT DAPI Iis7PutPropertyVariant(
24 __in IAppHostElement *pElement,
25 __in LPCWSTR wzPropName,
26 __in VARIANT vtPut
27 )
28{
29 HRESULT hr = S_OK;
30 IAppHostProperty *pProperty = NULL;
31 BSTR bstrPropName = NULL;
32
33 bstrPropName = ::SysAllocString(wzPropName);
34 IisExitOnNull(bstrPropName, hr, E_OUTOFMEMORY, "failed SysAllocString");
35
36 hr = pElement->GetPropertyByName(bstrPropName, &pProperty);
37 IisExitOnFailure(hr, "Failed to get property object for %ls", wzPropName);
38
39 hr = pProperty->put_Value(vtPut);
40 IisExitOnFailure(hr, "Failed to set property value for %ls", wzPropName);
41
42LExit:
43 ReleaseBSTR(bstrPropName);
44 // caller responsible for cleaning up variant vtPut
45 ReleaseObject(pProperty);
46
47 return hr;
48}
49
50extern "C" HRESULT DAPI Iis7PutPropertyString(
51 __in IAppHostElement *pElement,
52 __in LPCWSTR wzPropName,
53 __in LPCWSTR wzString
54 )
55{
56 HRESULT hr = S_OK;
57 VARIANT vtPut;
58
59 ::VariantInit(&vtPut);
60 vtPut.vt = VT_BSTR;
61 vtPut.bstrVal = ::SysAllocString(wzString);
62 IisExitOnNull(vtPut.bstrVal, hr, E_OUTOFMEMORY, "failed SysAllocString");
63
64 hr = Iis7PutPropertyVariant(pElement, wzPropName, vtPut);
65
66LExit:
67 ReleaseVariant(vtPut);
68
69 return hr;
70}
71
72extern "C" HRESULT DAPI Iis7PutPropertyInteger(
73 __in IAppHostElement *pElement,
74 __in LPCWSTR wzPropName,
75 __in DWORD dValue
76 )
77{
78 VARIANT vtPut;
79
80 ::VariantInit(&vtPut);
81 vtPut.vt = VT_I4;
82 vtPut.lVal = dValue;
83 return Iis7PutPropertyVariant(pElement, wzPropName, vtPut);
84}
85
86extern "C" HRESULT DAPI Iis7PutPropertyBool(
87 __in IAppHostElement *pElement,
88 __in LPCWSTR wzPropName,
89 __in BOOL fValue)
90{
91 VARIANT vtPut;
92
93 ::VariantInit(&vtPut);
94 vtPut.vt = VT_BOOL;
95 vtPut.boolVal = (fValue == FALSE) ? VARIANT_FALSE : VARIANT_TRUE;
96 return Iis7PutPropertyVariant(pElement, wzPropName, vtPut);
97}
98
99extern "C" HRESULT DAPI Iis7GetPropertyVariant(
100 __in IAppHostElement *pElement,
101 __in LPCWSTR wzPropName,
102 __in VARIANT* vtGet
103 )
104{
105 HRESULT hr = S_OK;
106 IAppHostProperty *pProperty = NULL;
107 BSTR bstrPropName = NULL;
108
109 bstrPropName = ::SysAllocString(wzPropName);
110 IisExitOnNull(bstrPropName, hr, E_OUTOFMEMORY, "failed SysAllocString");
111
112 hr = pElement->GetPropertyByName(bstrPropName, &pProperty);
113 IisExitOnFailure(hr, "Failed to get property object for %ls", wzPropName);
114
115 hr = pProperty->get_Value(vtGet);
116 IisExitOnFailure(hr, "Failed to get property value for %ls", wzPropName);
117
118LExit:
119 ReleaseBSTR(bstrPropName);
120 // caller responsible for cleaning up variant vtGet
121 ReleaseObject(pProperty);
122
123 return hr;
124}
125
126extern "C" HRESULT DAPI Iis7GetPropertyString(
127 __in IAppHostElement *pElement,
128 __in LPCWSTR wzPropName,
129 __in LPWSTR* psczGet
130 )
131{
132 HRESULT hr = S_OK;
133 VARIANT vtGet;
134
135 ::VariantInit(&vtGet);
136 hr = Iis7GetPropertyVariant(pElement, wzPropName, &vtGet);
137 IisExitOnFailure(hr, "Failed to get iis7 property variant with name: %ls", wzPropName);
138
139 if (!ISSTRINGVARIANT(vtGet.vt))
140 {
141 hr = E_UNEXPECTED;
142 IisExitOnFailure(hr, "Tried to get property as a string, but type was %d instead.", vtGet.vt);
143 }
144
145 hr = StrAllocString(psczGet, vtGet.bstrVal, 0);
146
147LExit:
148 ReleaseVariant(vtGet);
149
150 return hr;
151}
152
153BOOL DAPI CompareVariantDefault(
154 __in VARIANT* pVariant1,
155 __in VARIANT* pVariant2
156 )
157{
158 BOOL fEqual = FALSE;
159
160 switch (pVariant1->vt)
161 {
162 // VarCmp doesn't work for unsigned ints
163 // We'd like to allow signed/unsigned comparison as well since
164 // IIS doesn't document variant type for integer fields
165 case VT_I1:
166 case VT_UI1:
167 if (VT_I1 == pVariant2->vt || VT_UI1 == pVariant2->vt)
168 {
169 fEqual = pVariant1->bVal == pVariant2->bVal;
170 }
171 break;
172 case VT_I2:
173 case VT_UI2:
174 if (VT_I2 == pVariant2->vt || VT_UI2 == pVariant2->vt)
175 {
176 fEqual = pVariant1->uiVal == pVariant2->uiVal;
177 }
178 break;
179 case VT_UI4:
180 case VT_I4:
181 if (VT_I4 == pVariant2->vt || VT_UI4 == pVariant2->vt)
182 {
183 fEqual = pVariant1->ulVal == pVariant2->ulVal;
184 }
185 break;
186 case VT_UI8:
187 case VT_I8:
188 if (VT_I8 == pVariant2->vt || VT_UI8 == pVariant2->vt)
189 {
190 fEqual = pVariant1->ullVal == pVariant2->ullVal;
191 }
192 break;
193 default:
194 fEqual = VARCMP_EQ == ::VarCmp(pVariant1,
195 pVariant2,
196 LOCALE_INVARIANT,
197 NORM_IGNORECASE);
198 }
199
200 return fEqual;
201}
202
203BOOL DAPI CompareVariantPath(
204 __in VARIANT* pVariant1,
205 __in VARIANT* pVariant2
206 )
207{
208 HRESULT hr = S_OK;
209 BOOL fEqual = FALSE;
210 LPWSTR wzValue1 = NULL;
211 LPWSTR wzValue2 = NULL;
212
213 if (ISSTRINGVARIANT(pVariant1->vt))
214 {
215 hr = PathExpand(&wzValue1, pVariant1->bstrVal, PATH_EXPAND_ENVIRONMENT | PATH_EXPAND_FULLPATH);
216 IisExitOnFailure(hr, "Failed to expand path %ls", pVariant1->bstrVal);
217 }
218
219 if (ISSTRINGVARIANT(pVariant2->vt))
220 {
221 hr = PathExpand(&wzValue2, pVariant2->bstrVal, PATH_EXPAND_ENVIRONMENT | PATH_EXPAND_FULLPATH);
222 IisExitOnFailure(hr, "Failed to expand path %ls", pVariant2->bstrVal);
223 }
224
225 fEqual = CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, wzValue1, -1, wzValue2, -1);
226
227LExit:
228 ReleaseNullStr(wzValue1);
229 ReleaseNullStr(wzValue2);
230 return fEqual;
231}
232
233BOOL DAPI IsMatchingAppHostElementCallback(
234 __in IAppHostElement *pElement,
235 __in_bcount(sizeof(IIS7_APPHOSTELEMENTCOMPARISON)) LPVOID pContext
236 )
237{
238 IIS7_APPHOSTELEMENTCOMPARISON* pComparison = (IIS7_APPHOSTELEMENTCOMPARISON*) pContext;
239
240 return Iis7IsMatchingAppHostElement(pElement, pComparison);
241}
242
243extern "C" BOOL DAPI Iis7IsMatchingAppHostElement(
244 __in IAppHostElement *pElement,
245 __in IIS7_APPHOSTELEMENTCOMPARISON* pComparison
246 )
247{
248 HRESULT hr = S_OK;
249 BOOL fResult = FALSE;
250 IAppHostProperty *pProperty = NULL;
251 BSTR bstrElementName = NULL;
252
253 VARIANT vPropValue;
254 ::VariantInit(&vPropValue);
255
256 // Use the default comparator if a comparator is not specified
257 VARIANTCOMPARATORPROC pComparator = pComparison->pComparator ? pComparison->pComparator : CompareVariantDefault;
258
259 hr = pElement->get_Name(&bstrElementName);
260 IisExitOnFailure(hr, "Failed to get name of element");
261 if (CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pComparison->sczElementName, -1, bstrElementName, -1))
262 {
263 ExitFunction();
264 }
265
266 hr = Iis7GetPropertyVariant(pElement, pComparison->sczAttributeName, &vPropValue);
267 IisExitOnFailure(hr, "Failed to get value of %ls attribute of %ls element", pComparison->sczAttributeName, pComparison->sczElementName);
268
269 if (TRUE == pComparator(pComparison->pvAttributeValue, &vPropValue))
270 {
271 fResult = TRUE;
272 }
273
274LExit:
275 ReleaseBSTR(bstrElementName);
276 ReleaseVariant(vPropValue);
277 ReleaseObject(pProperty);
278
279 return fResult;
280}
281
282BOOL DAPI IsMatchingAppHostMethod(
283 __in IAppHostMethod *pMethod,
284 __in LPCWSTR wzMethodName
285 )
286{
287 HRESULT hr = S_OK;
288 BOOL fResult = FALSE;
289 BSTR bstrName = NULL;
290
291 hr = pMethod->get_Name(&bstrName);
292 IisExitOnFailure(hr, "Failed to get name of element");
293
294 Assert(bstrName);
295
296 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, wzMethodName, -1, bstrName, -1))
297 {
298 fResult = TRUE;
299 }
300
301LExit:
302 ReleaseBSTR(bstrName);
303
304 return fResult;
305}
306
307extern "C" HRESULT DAPI Iis7FindAppHostElementPath(
308 __in IAppHostElementCollection *pCollection,
309 __in LPCWSTR wzElementName,
310 __in LPCWSTR wzAttributeName,
311 __in LPCWSTR wzAttributeValue,
312 __out IAppHostElement** ppElement,
313 __out DWORD* pdwIndex
314 )
315{
316 HRESULT hr = S_OK;
317 IIS7_APPHOSTELEMENTCOMPARISON comparison = { };
318 VARIANT vtValue = { };
319 ::VariantInit(&vtValue);
320
321 vtValue.vt = VT_BSTR;
322 vtValue.bstrVal = ::SysAllocString(wzAttributeValue);
323 IisExitOnNull(vtValue.bstrVal, hr, E_OUTOFMEMORY, "failed SysAllocString");
324
325 comparison.sczElementName = wzElementName;
326 comparison.sczAttributeName = wzAttributeName;
327 comparison.pvAttributeValue = &vtValue;
328 comparison.pComparator = CompareVariantPath;
329
330 hr = Iis7EnumAppHostElements(pCollection,
331 IsMatchingAppHostElementCallback,
332 &comparison,
333 ppElement,
334 pdwIndex);
335
336LExit:
337 ReleaseVariant(vtValue);
338
339 return hr;
340}
341
342extern "C" HRESULT DAPI Iis7FindAppHostElementString(
343 __in IAppHostElementCollection *pCollection,
344 __in LPCWSTR wzElementName,
345 __in LPCWSTR wzAttributeName,
346 __in LPCWSTR wzAttributeValue,
347 __out IAppHostElement** ppElement,
348 __out DWORD* pdwIndex
349 )
350{
351 HRESULT hr = S_OK;
352 VARIANT vtValue;
353 ::VariantInit(&vtValue);
354
355 vtValue.vt = VT_BSTR;
356 vtValue.bstrVal = ::SysAllocString(wzAttributeValue);
357 IisExitOnNull(vtValue.bstrVal, hr, E_OUTOFMEMORY, "failed SysAllocString");
358
359 hr = Iis7FindAppHostElementVariant(pCollection,
360 wzElementName,
361 wzAttributeName,
362 &vtValue,
363 ppElement,
364 pdwIndex);
365
366LExit:
367 ReleaseVariant(vtValue);
368
369 return hr;
370}
371
372extern "C" HRESULT DAPI Iis7FindAppHostElementInteger(
373 __in IAppHostElementCollection *pCollection,
374 __in LPCWSTR wzElementName,
375 __in LPCWSTR wzAttributeName,
376 __in DWORD dwAttributeValue,
377 __out IAppHostElement** ppElement,
378 __out DWORD* pdwIndex
379 )
380{
381 HRESULT hr = S_OK;
382 VARIANT vtValue;
383 ::VariantInit(&vtValue);
384
385 vtValue.vt = VT_UI4;
386 vtValue.ulVal = dwAttributeValue;
387
388 hr = Iis7FindAppHostElementVariant(pCollection,
389 wzElementName,
390 wzAttributeName,
391 &vtValue,
392 ppElement,
393 pdwIndex);
394
395 ReleaseVariant(vtValue);
396
397 return hr;
398}
399
400extern "C" HRESULT DAPI Iis7FindAppHostElementVariant(
401 __in IAppHostElementCollection *pCollection,
402 __in LPCWSTR wzElementName,
403 __in LPCWSTR wzAttributeName,
404 __in VARIANT* pvAttributeValue,
405 __out IAppHostElement** ppElement,
406 __out DWORD* pdwIndex
407 )
408{
409 IIS7_APPHOSTELEMENTCOMPARISON comparison = { };
410 comparison.sczElementName = wzElementName;
411 comparison.sczAttributeName = wzAttributeName;
412 comparison.pvAttributeValue = pvAttributeValue;
413 comparison.pComparator = CompareVariantDefault;
414
415 return Iis7EnumAppHostElements(pCollection,
416 IsMatchingAppHostElementCallback,
417 &comparison,
418 ppElement,
419 pdwIndex);
420}
421
422extern "C" HRESULT DAPI Iis7EnumAppHostElements(
423 __in IAppHostElementCollection *pCollection,
424 __in ENUMAPHOSTELEMENTPROC pCallback,
425 __in LPVOID pContext,
426 __out IAppHostElement** ppElement,
427 __out DWORD* pdwIndex
428 )
429{
430 HRESULT hr = S_OK;
431 IAppHostElement *pElement = NULL;
432 DWORD dwElements = 0;
433
434 VARIANT vtIndex;
435 ::VariantInit(&vtIndex);
436
437 if (NULL != ppElement)
438 {
439 *ppElement = NULL;
440 }
441 if (NULL != pdwIndex)
442 {
443 *pdwIndex = MAXDWORD;
444 }
445
446 hr = pCollection->get_Count(&dwElements);
447 IisExitOnFailure(hr, "Failed get application IAppHostElementCollection count");
448
449 vtIndex.vt = VT_UI4;
450 for (DWORD i = 0; i < dwElements; ++i)
451 {
452 vtIndex.ulVal = i;
453 hr = pCollection->get_Item(vtIndex , &pElement);
454 IisExitOnFailure(hr, "Failed get IAppHostElement element");
455
456 if (pCallback(pElement, pContext))
457 {
458 if (NULL != ppElement)
459 {
460 *ppElement = pElement;
461 pElement = NULL;
462 }
463 if (NULL != pdwIndex)
464 {
465 *pdwIndex = i;
466 }
467 break;
468 }
469
470 ReleaseNullObject(pElement);
471 }
472
473LExit:
474 ReleaseObject(pElement);
475 ReleaseVariant(vtIndex);
476
477 return hr;
478}
479
480extern "C" HRESULT DAPI Iis7FindAppHostMethod(
481 __in IAppHostMethodCollection *pCollection,
482 __in LPCWSTR wzMethodName,
483 __out IAppHostMethod** ppMethod,
484 __out DWORD* pdwIndex
485 )
486{
487 HRESULT hr = S_OK;
488 IAppHostMethod *pMethod = NULL;
489 DWORD dwMethods = 0;
490
491 VARIANT vtIndex;
492 ::VariantInit(&vtIndex);
493
494 if (NULL != ppMethod)
495 {
496 *ppMethod = NULL;
497 }
498 if (NULL != pdwIndex)
499 {
500 *pdwIndex = MAXDWORD;
501 }
502
503 hr = pCollection->get_Count(&dwMethods);
504 IisExitOnFailure(hr, "Failed get application IAppHostMethodCollection count");
505
506 vtIndex.vt = VT_UI4;
507 for (DWORD i = 0; i < dwMethods; ++i)
508 {
509 vtIndex.ulVal = i;
510 hr = pCollection->get_Item(vtIndex , &pMethod);
511 IisExitOnFailure(hr, "Failed get IAppHostMethod element");
512
513 if (IsMatchingAppHostMethod(pMethod, wzMethodName))
514 {
515 if (NULL != ppMethod)
516 {
517 *ppMethod = pMethod;
518 pMethod = NULL;
519 }
520 if (NULL != pdwIndex)
521 {
522 *pdwIndex = i;
523 }
524 break;
525 }
526
527 ReleaseNullObject(pMethod);
528 }
529
530LExit:
531 ReleaseObject(pMethod);
532 ReleaseVariant(vtIndex);
533
534 return hr;
535}
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/aclutil.h b/src/libs/dutil/WixToolset.DUtil/inc/aclutil.h
new file mode 100644
index 00000000..ac03f9a8
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/aclutil.h
@@ -0,0 +1,154 @@
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 <aclapi.h>
6#include <sddl.h>
7
8#define ReleaseSid(x) if (x) { AclFreeSid(x); }
9#define ReleaseNullSid(x) if (x) { AclFreeSid(x); x = NULL; }
10
11#ifdef __cplusplus
12extern "C" {
13#endif
14
15// structs
16struct ACL_ACCESS
17{
18 BOOL fDenyAccess;
19 DWORD dwAccessMask;
20
21 // TODO: consider using a union
22 LPCWSTR pwzAccountName; // NOTE: the last three items in this structure are ignored if this is not NULL
23
24 SID_IDENTIFIER_AUTHORITY sia; // used if pwzAccountName is NULL
25 BYTE nSubAuthorityCount;
26 DWORD nSubAuthority[8];
27};
28
29struct ACL_ACE
30{
31 DWORD dwFlags;
32 DWORD dwMask;
33 PSID psid;
34};
35
36
37// functions
38HRESULT DAPI AclCheckAccess(
39 __in HANDLE hToken,
40 __in ACL_ACCESS* paa
41 );
42HRESULT DAPI AclCheckAdministratorAccess(
43 __in HANDLE hToken
44 );
45HRESULT DAPI AclCheckLocalSystemAccess(
46 __in HANDLE hToken
47 );
48
49HRESULT DAPI AclGetWellKnownSid(
50 __in WELL_KNOWN_SID_TYPE wkst,
51 __deref_out PSID* ppsid
52 );
53HRESULT DAPI AclGetAccountSid(
54 __in_opt LPCWSTR wzSystem,
55 __in_z LPCWSTR wzAccount,
56 __deref_out PSID* ppsid
57 );
58HRESULT DAPI AclGetAccountSidString(
59 __in_z LPCWSTR wzSystem,
60 __in_z LPCWSTR wzAccount,
61 __deref_out_z LPWSTR* ppwzSid
62 );
63
64HRESULT DAPI AclCreateDacl(
65 __in_ecount(cDeny) ACL_ACE rgaaDeny[],
66 __in DWORD cDeny,
67 __in_ecount(cAllow) ACL_ACE rgaaAllow[],
68 __in DWORD cAllow,
69 __deref_out ACL** ppAcl
70 );
71HRESULT DAPI AclAddToDacl(
72 __in ACL* pAcl,
73 __in_ecount_opt(cDeny) const ACL_ACE rgaaDeny[],
74 __in DWORD cDeny,
75 __in_ecount_opt(cAllow) const ACL_ACE rgaaAllow[],
76 __in DWORD cAllow,
77 __deref_out ACL** ppAclNew
78 );
79HRESULT DAPI AclMergeDacls(
80 __in const ACL* pAcl1,
81 __in const ACL* pAcl2,
82 __deref_out ACL** ppAclNew
83 );
84HRESULT DAPI AclCreateDaclOld(
85 __in_ecount(cAclAccesses) ACL_ACCESS* paa,
86 __in DWORD cAclAccesses,
87 __deref_out ACL** ppAcl
88 );
89HRESULT DAPI AclCreateSecurityDescriptor(
90 __in_ecount(cAclAccesses) ACL_ACCESS* paa,
91 __in DWORD cAclAccesses,
92 __deref_out SECURITY_DESCRIPTOR** ppsd
93 );
94HRESULT DAPI AclCreateSecurityDescriptorFromDacl(
95 __in ACL* pACL,
96 __deref_out SECURITY_DESCRIPTOR** ppsd
97 );
98HRESULT __cdecl AclCreateSecurityDescriptorFromString(
99 __deref_out SECURITY_DESCRIPTOR** ppsd,
100 __in_z __format_string LPCWSTR wzSddlFormat,
101 ...
102 );
103HRESULT DAPI AclDuplicateSecurityDescriptor(
104 __in SECURITY_DESCRIPTOR* psd,
105 __deref_out SECURITY_DESCRIPTOR** ppsd
106 );
107HRESULT DAPI AclGetSecurityDescriptor(
108 __in_z LPCWSTR wzObject,
109 __in SE_OBJECT_TYPE sot,
110 __in SECURITY_INFORMATION securityInformation,
111 __deref_out SECURITY_DESCRIPTOR** ppsd
112 );
113HRESULT DAPI AclSetSecurityWithRetry(
114 __in_z LPCWSTR wzObject,
115 __in SE_OBJECT_TYPE sot,
116 __in SECURITY_INFORMATION securityInformation,
117 __in_opt PSID psidOwner,
118 __in_opt PSID psidGroup,
119 __in_opt PACL pDacl,
120 __in_opt PACL pSacl,
121 __in DWORD cRetry,
122 __in DWORD dwWaitMilliseconds
123 );
124
125HRESULT DAPI AclFreeSid(
126 __in PSID psid
127 );
128HRESULT DAPI AclFreeDacl(
129 __in ACL* pACL
130 );
131HRESULT DAPI AclFreeSecurityDescriptor(
132 __in SECURITY_DESCRIPTOR* psd
133 );
134
135HRESULT DAPI AclAddAdminToSecurityDescriptor(
136 __in SECURITY_DESCRIPTOR* pSecurity,
137 __deref_out SECURITY_DESCRIPTOR** ppSecurityNew
138 );
139
140// Following code in acl2util.cpp due to dependency on crypt32.dll.
141HRESULT DAPI AclCalculateServiceSidString(
142 __in LPCWSTR wzServiceName,
143 __in SIZE_T cchServiceName,
144 __deref_out_z LPWSTR* psczSid
145 );
146HRESULT DAPI AclGetAccountSidStringEx(
147 __in_z LPCWSTR wzSystem,
148 __in_z LPCWSTR wzAccount,
149 __deref_out_z LPWSTR* psczSid
150 );
151
152#ifdef __cplusplus
153}
154#endif
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/apputil.h b/src/libs/dutil/WixToolset.DUtil/inc/apputil.h
new file mode 100644
index 00000000..1a1e14f7
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/apputil.h
@@ -0,0 +1,45 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9// functions
10
11/********************************************************************
12AppFreeCommandLineArgs - frees argv from AppParseCommandLine.
13
14********************************************************************/
15void DAPI AppFreeCommandLineArgs(
16 __in LPWSTR* argv
17 );
18
19void DAPI AppInitialize(
20 __in_ecount(cSafelyLoadSystemDlls) LPCWSTR rgsczSafelyLoadSystemDlls[],
21 __in DWORD cSafelyLoadSystemDlls
22 );
23
24/********************************************************************
25AppInitializeUnsafe - initializes without the full standard safety
26 precautions for an application.
27
28********************************************************************/
29void DAPI AppInitializeUnsafe();
30
31/********************************************************************
32AppParseCommandLine - parses the command line using CommandLineToArgvW.
33 The caller must free the value of pArgv on success
34 by calling AppFreeCommandLineArgs.
35
36********************************************************************/
37DAPI_(HRESULT) AppParseCommandLine(
38 __in LPCWSTR wzCommandLine,
39 __in int* argc,
40 __in LPWSTR** pArgv
41 );
42
43#ifdef __cplusplus
44}
45#endif
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/apuputil.h b/src/libs/dutil/WixToolset.DUtil/inc/apuputil.h
new file mode 100644
index 00000000..f26a12b7
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/apuputil.h
@@ -0,0 +1,87 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9#define ReleaseApupChain(p) if (p) { ApupFreeChain(p); p = NULL; }
10#define ReleaseNullApupChain(p) if (p) { ApupFreeChain(p); p = NULL; }
11
12
13const LPCWSTR APPLICATION_SYNDICATION_NAMESPACE = L"http://appsyndication.org/2006/appsyn";
14
15typedef enum APUP_HASH_ALGORITHM
16{
17 APUP_HASH_ALGORITHM_UNKNOWN,
18 APUP_HASH_ALGORITHM_MD5,
19 APUP_HASH_ALGORITHM_SHA1,
20 APUP_HASH_ALGORITHM_SHA256,
21 APUP_HASH_ALGORITHM_SHA512,
22} APUP_HASH_ALGORITHM;
23
24
25struct APPLICATION_UPDATE_ENCLOSURE
26{
27 LPWSTR wzUrl;
28 LPWSTR wzLocalName;
29 DWORD64 dw64Size;
30
31 BYTE* rgbDigest;
32 DWORD cbDigest;
33 APUP_HASH_ALGORITHM digestAlgorithm;
34
35 BOOL fInstaller;
36};
37
38
39struct APPLICATION_UPDATE_ENTRY
40{
41 LPWSTR wzApplicationId;
42 LPWSTR wzApplicationType;
43 LPWSTR wzTitle;
44 LPWSTR wzSummary;
45 LPWSTR wzContentType;
46 LPWSTR wzContent;
47
48 LPWSTR wzUpgradeId;
49 BOOL fUpgradeExclusive;
50 VERUTIL_VERSION* pVersion;
51 VERUTIL_VERSION* pUpgradeVersion;
52
53 DWORD64 dw64TotalSize;
54
55 DWORD cEnclosures;
56 APPLICATION_UPDATE_ENCLOSURE* rgEnclosures;
57};
58
59
60struct APPLICATION_UPDATE_CHAIN
61{
62 LPWSTR wzDefaultApplicationId;
63 LPWSTR wzDefaultApplicationType;
64
65 DWORD cEntries;
66 APPLICATION_UPDATE_ENTRY* rgEntries;
67};
68
69
70HRESULT DAPI ApupAllocChainFromAtom(
71 __in ATOM_FEED* pFeed,
72 __out APPLICATION_UPDATE_CHAIN** ppChain
73 );
74
75HRESULT DAPI ApupFilterChain(
76 __in APPLICATION_UPDATE_CHAIN* pChain,
77 __in VERUTIL_VERSION* pVersion,
78 __out APPLICATION_UPDATE_CHAIN** ppFilteredChain
79 );
80
81void DAPI ApupFreeChain(
82 __in APPLICATION_UPDATE_CHAIN* pChain
83 );
84
85#ifdef __cplusplus
86}
87#endif
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/atomutil.h b/src/libs/dutil/WixToolset.DUtil/inc/atomutil.h
new file mode 100644
index 00000000..9acfc1d5
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/atomutil.h
@@ -0,0 +1,146 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9#define ReleaseAtomFeed(p) if (p) { AtomFreeFeed(p); }
10#define ReleaseNullAtomFeed(p) if (p) { AtomFreeFeed(p); p = NULL; }
11
12
13struct ATOM_UNKNOWN_ATTRIBUTE
14{
15 LPWSTR wzNamespace;
16 LPWSTR wzAttribute;
17 LPWSTR wzValue;
18
19 ATOM_UNKNOWN_ATTRIBUTE* pNext;
20};
21
22struct ATOM_UNKNOWN_ELEMENT
23{
24 LPWSTR wzNamespace;
25 LPWSTR wzElement;
26 LPWSTR wzValue;
27
28 ATOM_UNKNOWN_ATTRIBUTE* pAttributes;
29 ATOM_UNKNOWN_ELEMENT* pNext;
30};
31
32struct ATOM_LINK
33{
34 LPWSTR wzRel;
35 LPWSTR wzTitle;
36 LPWSTR wzType;
37 LPWSTR wzUrl;
38 LPWSTR wzValue;
39 DWORD64 dw64Length;
40
41 ATOM_UNKNOWN_ATTRIBUTE* pUnknownAttributes;
42 ATOM_UNKNOWN_ELEMENT* pUnknownElements;
43};
44
45struct ATOM_CONTENT
46{
47 LPWSTR wzType;
48 LPWSTR wzUrl;
49 LPWSTR wzValue;
50
51 ATOM_UNKNOWN_ELEMENT* pUnknownElements;
52};
53
54struct ATOM_AUTHOR
55{
56 LPWSTR wzName;
57 LPWSTR wzEmail;
58 LPWSTR wzUrl;
59};
60
61struct ATOM_CATEGORY
62{
63 LPWSTR wzLabel;
64 LPWSTR wzScheme;
65 LPWSTR wzTerm;
66
67 ATOM_UNKNOWN_ELEMENT* pUnknownElements;
68};
69
70struct ATOM_ENTRY
71{
72 LPWSTR wzId;
73 LPWSTR wzSummary;
74 LPWSTR wzTitle;
75 FILETIME ftPublished;
76 FILETIME ftUpdated;
77
78 ATOM_CONTENT* pContent;
79
80 DWORD cAuthors;
81 ATOM_AUTHOR* rgAuthors;
82
83 DWORD cCategories;
84 ATOM_CATEGORY* rgCategories;
85
86 DWORD cLinks;
87 ATOM_LINK* rgLinks;
88
89 IXMLDOMNode* pixn;
90 ATOM_UNKNOWN_ELEMENT* pUnknownElements;
91};
92
93struct ATOM_FEED
94{
95 LPWSTR wzGenerator;
96 LPWSTR wzIcon;
97 LPWSTR wzId;
98 LPWSTR wzLogo;
99 LPWSTR wzSubtitle;
100 LPWSTR wzTitle;
101 FILETIME ftUpdated;
102
103 DWORD cAuthors;
104 ATOM_AUTHOR* rgAuthors;
105
106 DWORD cCategories;
107 ATOM_CATEGORY* rgCategories;
108
109 DWORD cEntries;
110 ATOM_ENTRY* rgEntries;
111
112 DWORD cLinks;
113 ATOM_LINK* rgLinks;
114
115 IXMLDOMNode* pixn;
116 ATOM_UNKNOWN_ELEMENT* pUnknownElements;
117};
118
119HRESULT DAPI AtomInitialize(
120 );
121
122void DAPI AtomUninitialize(
123 );
124
125HRESULT DAPI AtomParseFromString(
126 __in_z LPCWSTR wzAtomString,
127 __out ATOM_FEED **ppFeed
128 );
129
130HRESULT DAPI AtomParseFromFile(
131 __in_z LPCWSTR wzAtomFile,
132 __out ATOM_FEED **ppFeed
133 );
134
135HRESULT DAPI AtomParseFromDocument(
136 __in IXMLDOMDocument* pixdDocument,
137 __out ATOM_FEED **ppFeed
138 );
139
140void DAPI AtomFreeFeed(
141 __in_xcount(pFeed->cItems) ATOM_FEED* pFeed
142 );
143
144#ifdef __cplusplus
145}
146#endif
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/buffutil.h b/src/libs/dutil/WixToolset.DUtil/inc/buffutil.h
new file mode 100644
index 00000000..322209e6
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/buffutil.h
@@ -0,0 +1,91 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9
10// macro definitions
11
12#define ReleaseBuffer ReleaseMem
13#define ReleaseNullBuffer ReleaseNullMem
14#define BuffFree MemFree
15
16
17// function declarations
18
19HRESULT BuffReadNumber(
20 __in_bcount(cbBuffer) const BYTE* pbBuffer,
21 __in SIZE_T cbBuffer,
22 __inout SIZE_T* piBuffer,
23 __out DWORD* pdw
24 );
25HRESULT BuffReadNumber64(
26 __in_bcount(cbBuffer) const BYTE* pbBuffer,
27 __in SIZE_T cbBuffer,
28 __inout SIZE_T* piBuffer,
29 __out DWORD64* pdw64
30 );
31HRESULT BuffReadPointer(
32 __in_bcount(cbBuffer) const BYTE* pbBuffer,
33 __in SIZE_T cbBuffer,
34 __inout SIZE_T* piBuffer,
35 __out DWORD_PTR* pdw
36);
37HRESULT BuffReadString(
38 __in_bcount(cbBuffer) const BYTE* pbBuffer,
39 __in SIZE_T cbBuffer,
40 __inout SIZE_T* piBuffer,
41 __deref_out_z LPWSTR* pscz
42 );
43HRESULT BuffReadStringAnsi(
44 __in_bcount(cbBuffer) const BYTE* pbBuffer,
45 __in SIZE_T cbBuffer,
46 __inout SIZE_T* piBuffer,
47 __deref_out_z LPSTR* pscz
48 );
49HRESULT BuffReadStream(
50 __in_bcount(cbBuffer) const BYTE* pbBuffer,
51 __in SIZE_T cbBuffer,
52 __inout SIZE_T* piBuffer,
53 __deref_inout_bcount(*pcbStream) BYTE** ppbStream,
54 __out SIZE_T* pcbStream
55 );
56
57HRESULT BuffWriteNumber(
58 __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer,
59 __inout SIZE_T* piBuffer,
60 __in DWORD dw
61 );
62HRESULT BuffWriteNumber64(
63 __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer,
64 __inout SIZE_T* piBuffer,
65 __in DWORD64 dw64
66 );
67HRESULT BuffWritePointer(
68 __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer,
69 __inout SIZE_T* piBuffer,
70 __in DWORD_PTR dw
71);
72HRESULT BuffWriteString(
73 __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer,
74 __inout SIZE_T* piBuffer,
75 __in_z_opt LPCWSTR scz
76 );
77HRESULT BuffWriteStringAnsi(
78 __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer,
79 __inout SIZE_T* piBuffer,
80 __in_z_opt LPCSTR scz
81 );
82HRESULT BuffWriteStream(
83 __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer,
84 __inout SIZE_T* piBuffer,
85 __in_bcount(cbStream) const BYTE* pbStream,
86 __in SIZE_T cbStream
87 );
88
89#ifdef __cplusplus
90}
91#endif
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/butil.h b/src/libs/dutil/WixToolset.DUtil/inc/butil.h
new file mode 100644
index 00000000..d1ec73bc
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/butil.h
@@ -0,0 +1,60 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9enum BUNDLE_INSTALL_CONTEXT
10{
11 BUNDLE_INSTALL_CONTEXT_MACHINE,
12 BUNDLE_INSTALL_CONTEXT_USER,
13};
14
15
16/********************************************************************
17BundleGetBundleInfo - Queries the bundle installation metadata for a given property
18
19RETURNS:
20 E_INVALIDARG
21 An invalid parameter was passed to the function.
22 HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT)
23 The bundle is not installed
24 HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY)
25 The property is unrecognized
26 HRESULT_FROM_WIN32(ERROR_MORE_DATA)
27 A buffer is too small to hold the requested data.
28 E_NOTIMPL:
29 Tried to read a bundle attribute for a type which has not been implemented
30
31 All other returns are unexpected returns from other dutil methods.
32********************************************************************/
33HRESULT DAPI BundleGetBundleInfo(
34 __in_z LPCWSTR szBundleId, // Bundle code
35 __in_z LPCWSTR szAttribute, // attribute name
36 __out_ecount_opt(*pcchValueBuf) LPWSTR lpValueBuf, // returned value, NULL if not desired
37 __inout_opt LPDWORD pcchValueBuf // in/out buffer character count
38 );
39
40/********************************************************************
41BundleEnumRelatedBundle - Queries the bundle installation metadata for installs with the given upgrade code
42
43NOTE: lpBundleIdBuff is a buffer to receive the bundle GUID. This buffer must be 39 characters long.
44 The first 38 characters are for the GUID, and the last character is for the terminating null character.
45RETURNS:
46 E_INVALIDARG
47 An invalid parameter was passed to the function.
48
49 All other returns are unexpected returns from other dutil methods.
50********************************************************************/
51HRESULT DAPI BundleEnumRelatedBundle(
52 __in_z LPCWSTR lpUpgradeCode,
53 __in BUNDLE_INSTALL_CONTEXT context,
54 __inout PDWORD pdwStartIndex,
55 __out_ecount(MAX_GUID_CHARS+1) LPWSTR lpBundleIdBuf
56 );
57
58#ifdef __cplusplus
59}
60#endif
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/cabcutil.h b/src/libs/dutil/WixToolset.DUtil/inc/cabcutil.h
new file mode 100644
index 00000000..4f0c7b13
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/cabcutil.h
@@ -0,0 +1,62 @@
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 <fci.h>
6#include <fcntl.h>
7#include <msi.h>
8
9// Callback from PFNFCIGETNEXTCABINET CabCGetNextCabinet method
10// First argument is the name of splitting cabinet without extension e.g. "cab1"
11// Second argument is name of the new cabinet that would be formed by splitting e.g. "cab1b.cab"
12// Third argument is the file token of the first file present in the splitting cabinet
13typedef void (__stdcall * FileSplitCabNamesCallback)(LPWSTR, LPWSTR, LPWSTR);
14
15#define CAB_MAX_SIZE 0x7FFFFFFF // (see KB: Q174866)
16
17#ifdef __cplusplus
18extern "C" {
19#endif
20
21extern const int CABC_HANDLE_BYTES;
22
23// time vs. space trade-off
24typedef enum COMPRESSION_TYPE
25{
26 COMPRESSION_TYPE_NONE, // fastest
27 COMPRESSION_TYPE_LOW,
28 COMPRESSION_TYPE_MEDIUM,
29 COMPRESSION_TYPE_HIGH, // smallest
30 COMPRESSION_TYPE_MSZIP
31} COMPRESSION_TYPE;
32
33// functions
34HRESULT DAPI CabCBegin(
35 __in_z LPCWSTR wzCab,
36 __in_z LPCWSTR wzCabDir,
37 __in DWORD dwMaxFiles,
38 __in DWORD dwMaxSize,
39 __in DWORD dwMaxThresh,
40 __in COMPRESSION_TYPE ct,
41 __out_bcount(CABC_HANDLE_BYTES) HANDLE *phContext
42 );
43HRESULT DAPI CabCNextCab(
44 __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext
45 );
46HRESULT DAPI CabCAddFile(
47 __in_z LPCWSTR wzFile,
48 __in_z_opt LPCWSTR wzToken,
49 __in_opt PMSIFILEHASHINFO pmfHash,
50 __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext
51 );
52HRESULT DAPI CabCFinish(
53 __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext,
54 __in_opt FileSplitCabNamesCallback fileSplitCabNamesCallback
55 );
56void DAPI CabCCancel(
57 __in_bcount(CABC_HANDLE_BYTES) HANDLE hContext
58 );
59
60#ifdef __cplusplus
61}
62#endif
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/cabutil.h b/src/libs/dutil/WixToolset.DUtil/inc/cabutil.h
new file mode 100644
index 00000000..0bedba80
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/cabutil.h
@@ -0,0 +1,56 @@
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 <fdi.h>
6#include <sys\stat.h>
7
8#ifdef __cplusplus
9extern "C" {
10#endif
11
12// structs
13
14
15// callback function prototypes
16typedef HRESULT (*CAB_CALLBACK_OPEN_FILE)(LPCWSTR wzFile, INT_PTR* ppFile);
17typedef HRESULT (*CAB_CALLBACK_READ_FILE)(INT_PTR pFile, LPVOID pvData, DWORD cbData, DWORD* pcbRead);
18typedef HRESULT (*CAB_CALLBACK_WRITE_FILE)(INT_PTR pFile, LPVOID pvData, DWORD cbData, DWORD* pcbRead);
19typedef LONG (*CAB_CALLBACK_SEEK_FILE)(INT_PTR pFile, DWORD dwMove, DWORD dwMoveMethod);
20typedef HRESULT (*CAB_CALLBACK_CLOSE_FILE)(INT_PTR pFile);
21
22typedef HRESULT (*CAB_CALLBACK_BEGIN_FILE)(LPCWSTR wzFileId, FILETIME* pftFileTime, DWORD cbFileSize, LPVOID pvContext, INT_PTR* ppFile);
23typedef HRESULT (*CAB_CALLBACK_END_FILE)(LPCWSTR wzFileId, LPVOID pvContext, INT_PTR pFile);
24typedef HRESULT (*CAB_CALLBACK_PROGRESS)(BOOL fBeginFile, LPCWSTR wzFileId, LPVOID pvContext);
25
26// function type with calling convention of __stdcall that .NET 1.1 understands only
27// .NET 2.0 will not need this
28typedef INT_PTR (FAR __stdcall *STDCALL_PFNFDINOTIFY)(FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin);
29
30
31// functions
32HRESULT DAPI CabInitialize(
33 __in BOOL fDelayLoad
34 );
35void DAPI CabUninitialize(
36 );
37
38HRESULT DAPI CabExtract(
39 __in_z LPCWSTR wzCabinet,
40 __in_z LPCWSTR wzExtractFile,
41 __in_z LPCWSTR wzExtractDir,
42 __in_opt CAB_CALLBACK_PROGRESS pfnProgress,
43 __in_opt LPVOID pvContext,
44 __in DWORD64 dw64EmbeddedOffset
45 );
46
47HRESULT DAPI CabEnumerate(
48 __in_z LPCWSTR wzCabinet,
49 __in_z LPCWSTR wzEnumerateFile,
50 __in STDCALL_PFNFDINOTIFY pfnNotify,
51 __in DWORD64 dw64EmbeddedOffset
52 );
53
54#ifdef __cplusplus
55}
56#endif
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/certutil.h b/src/libs/dutil/WixToolset.DUtil/inc/certutil.h
new file mode 100644
index 00000000..8565c1cf
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/certutil.h
@@ -0,0 +1,66 @@
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 ReleaseCertStore(p) if (p) { ::CertCloseStore(p, 0); p = NULL; }
6#define ReleaseCertContext(p) if (p) { ::CertFreeCertificateContext(p); p = NULL; }
7#define ReleaseCertChain(p) if (p) { ::CertFreeCertificateChain(p); p = NULL; }
8
9#ifdef __cplusplus
10extern "C" {
11#endif
12
13HRESULT DAPI CertReadProperty(
14 __in PCCERT_CONTEXT pCertContext,
15 __in DWORD dwProperty,
16 __out_bcount(*pcbValue) LPVOID pvValue,
17 __out_opt DWORD* pcbValue
18 );
19
20HRESULT DAPI CertGetAuthenticodeSigningTimestamp(
21 __in CMSG_SIGNER_INFO* pSignerInfo,
22 __out FILETIME* pft
23 );
24
25HRESULT DAPI GetCryptProvFromCert(
26 __in_opt HWND hwnd,
27 __in PCCERT_CONTEXT pCert,
28 __out HCRYPTPROV *phCryptProv,
29 __out DWORD *pdwKeySpec,
30 __in BOOL *pfDidCryptAcquire,
31 __deref_opt_out LPWSTR *ppwszTmpContainer,
32 __deref_opt_out LPWSTR *ppwszProviderName,
33 __out DWORD *pdwProviderType
34 );
35
36HRESULT DAPI FreeCryptProvFromCert(
37 __in BOOL fAcquired,
38 __in HCRYPTPROV hProv,
39 __in_opt LPWSTR pwszCapiProvider,
40 __in DWORD dwProviderType,
41 __in_opt LPWSTR pwszTmpContainer
42 );
43
44HRESULT DAPI GetProvSecurityDesc(
45 __in HCRYPTPROV hProv,
46 __deref_out SECURITY_DESCRIPTOR** pSecurity
47 );
48
49HRESULT DAPI SetProvSecurityDesc(
50 __in HCRYPTPROV hProv,
51 __in SECURITY_DESCRIPTOR* pSecurity
52 );
53
54BOOL DAPI CertHasPrivateKey(
55 __in PCCERT_CONTEXT pCertContext,
56 __out_opt DWORD* pdwKeySpec
57 );
58
59HRESULT DAPI CertInstallSingleCertificate(
60 __in HCERTSTORE hStore,
61 __in PCCERT_CONTEXT pCertContext,
62 __in LPCWSTR wzName
63 );
64#ifdef __cplusplus
65}
66#endif
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/conutil.h b/src/libs/dutil/WixToolset.DUtil/inc/conutil.h
new file mode 100644
index 00000000..38aaea84
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/conutil.h
@@ -0,0 +1,80 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9#define ConsoleExitOnFailureSource(d, x, c, f, ...) if (FAILED(x)) { ConsoleWriteError(x, c, f, __VA_ARGS__); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; }
10#define ConsoleExitOnLastErrorSource(d, x, c, f, ...) { x = ::GetLastError(); x = HRESULT_FROM_WIN32(x); if (FAILED(x)) { ConsoleWriteError(x, c, f, __VA_ARGS__); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; } }
11#define ConsoleExitOnNullSource(d, p, x, e, c, f, ...) if (NULL == p) { x = e; ConsoleWriteError(x, c, f, __VA_ARGS__); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; }
12#define ConsoleExitOnNullWithLastErrorSource(d, p, x, c, f, ...) if (NULL == p) { DWORD Dutil_er = ::GetLastError(); x = HRESULT_FROM_WIN32(Dutil_er); if (!FAILED(x)) { x = E_FAIL; } ConsoleWriteError(x, c, f, __VA_ARGS__); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; }
13#define ConsoleExitWithLastErrorSource(d, x, c, f, ...) { DWORD Dutil_er = ::GetLastError(); x = HRESULT_FROM_WIN32(Dutil_er); if (!FAILED(x)) { x = E_FAIL; } ConsoleWriteError(x, c, f, __VA_ARGS__); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; }
14
15
16#define ConsoleExitOnFailure(x, c, f, ...) ConsoleExitOnFailureSource(DUTIL_SOURCE_DEFAULT, x, c, f, __VA_ARGS__)
17#define ConsoleExitOnLastError(x, c, f, ...) ConsoleExitOnLastErrorSource(DUTIL_SOURCE_DEFAULT, x, c, f, __VA_ARGS__)
18#define ConsoleExitOnNull(p, x, e, c, f, ...) ConsoleExitOnNullSource(DUTIL_SOURCE_DEFAULT, p, x, e, c, f, __VA_ARGS__)
19#define ConsoleExitOnNullWithLastError(p, x, c, f, ...) ConsoleExitOnNullWithLastErrorSource(DUTIL_SOURCE_DEFAULT, p, x, c, f, __VA_ARGS__)
20#define ConsoleExitWithLastError(x, c, f, ...) ConsoleExitWithLastErrorSource(DUTIL_SOURCE_DEFAULT, x, c, f, __VA_ARGS__)
21
22// enums
23typedef enum CONSOLE_COLOR { CONSOLE_COLOR_NORMAL, CONSOLE_COLOR_RED, CONSOLE_COLOR_YELLOW, CONSOLE_COLOR_GREEN } CONSOLE_COLOR;
24
25// structs
26
27// functions
28HRESULT DAPI ConsoleInitialize();
29void DAPI ConsoleUninitialize();
30
31void DAPI ConsoleGreen();
32void DAPI ConsoleRed();
33void DAPI ConsoleYellow();
34void DAPI ConsoleNormal();
35
36HRESULT DAPI ConsoleWrite(
37 CONSOLE_COLOR cc,
38 __in_z __format_string LPCSTR szFormat,
39 ...
40 );
41HRESULT DAPI ConsoleWriteLine(
42 CONSOLE_COLOR cc,
43 __in_z __format_string LPCSTR szFormat,
44 ...
45 );
46HRESULT DAPI ConsoleWriteError(
47 HRESULT hrError,
48 CONSOLE_COLOR cc,
49 __in_z __format_string LPCSTR szFormat,
50 ...
51 );
52
53HRESULT DAPI ConsoleReadW(
54 __deref_out_z LPWSTR* ppwzBuffer
55 );
56
57HRESULT DAPI ConsoleReadStringA(
58 __deref_inout_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPSTR* szCharBuffer,
59 CONST DWORD cchCharBuffer,
60 __out DWORD* pcchNumCharReturn
61 );
62HRESULT DAPI ConsoleReadStringW(
63 __deref_inout_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPWSTR* szCharBuffer,
64 CONST DWORD cchCharBuffer,
65 __out DWORD* pcchNumCharReturn
66 );
67
68HRESULT DAPI ConsoleReadNonBlockingW(
69 __deref_out_ecount_opt(*pcchSize) LPWSTR* ppwzBuffer,
70 __out DWORD* pcchSize,
71 BOOL fReadLine
72 );
73
74HRESULT DAPI ConsoleSetReadHidden(void);
75HRESULT DAPI ConsoleSetReadNormal(void);
76
77#ifdef __cplusplus
78}
79#endif
80
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/cryputil.h b/src/libs/dutil/WixToolset.DUtil/inc/cryputil.h
new file mode 100644
index 00000000..02492d8a
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/cryputil.h
@@ -0,0 +1,106 @@
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 ReleaseCryptMsg(p) if (p) { ::CryptMsgClose(p); p = NULL; }
6
7#ifdef __cplusplus
8extern "C" {
9#endif
10
11
12// Use CRYPTPROTECTMEMORY_BLOCK_SIZE, because it's larger and thus more restrictive than RTL_ENCRYPT_MEMORY_SIZE.
13#define CRYP_ENCRYPT_MEMORY_SIZE CRYPTPROTECTMEMORY_BLOCK_SIZE
14#define MD5_HASH_LEN 16
15#define SHA1_HASH_LEN 20
16#define SHA256_HASH_LEN 32
17#define SHA512_HASH_LEN 64
18
19typedef NTSTATUS (APIENTRY *PFN_RTLENCRYPTMEMORY)(
20 __inout PVOID Memory,
21 __in ULONG MemoryLength,
22 __in ULONG OptionFlags
23 );
24
25typedef NTSTATUS (APIENTRY *PFN_RTLDECRYPTMEMORY)(
26 __inout PVOID Memory,
27 __in ULONG MemoryLength,
28 __in ULONG OptionFlags
29 );
30
31typedef BOOL (APIENTRY *PFN_CRYPTPROTECTMEMORY)(
32 __inout LPVOID pData,
33 __in DWORD cbData,
34 __in DWORD dwFlags
35 );
36
37typedef BOOL (APIENTRY *PFN_CRYPTUNPROTECTMEMORY)(
38 __inout LPVOID pData,
39 __in DWORD cbData,
40 __in DWORD dwFlags
41 );
42
43// function declarations
44
45HRESULT DAPI CrypInitialize();
46void DAPI CrypUninitialize();
47
48HRESULT DAPI CrypDecodeObject(
49 __in_z LPCSTR szStructType,
50 __in_ecount(cbData) const BYTE* pbData,
51 __in DWORD cbData,
52 __in DWORD dwFlags,
53 __out LPVOID* ppvObject,
54 __out_opt DWORD* pcbObject
55 );
56
57HRESULT DAPI CrypMsgGetParam(
58 __in HCRYPTMSG hCryptMsg,
59 __in DWORD dwType,
60 __in DWORD dwIndex,
61 __out LPVOID* ppvData,
62 __out_opt DWORD* pcbData
63 );
64
65HRESULT DAPI CrypHashFile(
66 __in_z LPCWSTR wzFilePath,
67 __in DWORD dwProvType,
68 __in ALG_ID algid,
69 __out_bcount(cbHash) BYTE* pbHash,
70 __in DWORD cbHash,
71 __out_opt DWORD64* pqwBytesHashed
72 );
73
74HRESULT DAPI CrypHashFileHandle(
75 __in HANDLE hFile,
76 __in DWORD dwProvType,
77 __in ALG_ID algid,
78 __out_bcount(cbHash) BYTE* pbHash,
79 __in DWORD cbHash,
80 __out_opt DWORD64* pqwBytesHashed
81 );
82
83HRESULT DAPI CrypHashBuffer(
84 __in_bcount(cbBuffer) const BYTE* pbBuffer,
85 __in SIZE_T cbBuffer,
86 __in DWORD dwProvType,
87 __in ALG_ID algid,
88 __out_bcount(cbHash) BYTE* pbHash,
89 __in DWORD cbHash
90 );
91
92HRESULT DAPI CrypEncryptMemory(
93 __inout LPVOID pData,
94 __in DWORD cbData,
95 __in DWORD dwFlags
96 );
97
98HRESULT DAPI CrypDecryptMemory(
99 __inout LPVOID pData,
100 __in DWORD cbData,
101 __in DWORD dwFlags
102 );
103
104#ifdef __cplusplus
105}
106#endif
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/deputil.h b/src/libs/dutil/WixToolset.DUtil/inc/deputil.h
new file mode 100644
index 00000000..bfe235f3
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/deputil.h
@@ -0,0 +1,147 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9#define ReleaseDependencyArray(rg, c) if (rg) { DepDependencyArrayFree(rg, c); }
10#define ReleaseNullDependencyArray(rg, c) if (rg) { DepDependencyArrayFree(rg, c); rg = NULL; }
11
12typedef struct _DEPENDENCY
13{
14 LPWSTR sczKey;
15 LPWSTR sczName;
16} DEPENDENCY;
17
18
19/***************************************************************************
20 DepGetProviderInformation - gets the various pieces of data registered
21 with a dependency.
22
23 Note: Returns E_NOTFOUND if the dependency was not found.
24***************************************************************************/
25DAPI_(HRESULT) DepGetProviderInformation(
26 __in HKEY hkHive,
27 __in_z LPCWSTR wzProviderKey,
28 __deref_out_z_opt LPWSTR* psczId,
29 __deref_out_z_opt LPWSTR* psczName,
30 __deref_out_z_opt LPWSTR* psczVersion
31 );
32
33/***************************************************************************
34 DepCheckDependency - Checks that the dependency is registered and within
35 the proper version range.
36
37 Note: Returns E_NOTFOUND if the dependency was not found.
38***************************************************************************/
39DAPI_(HRESULT) DepCheckDependency(
40 __in HKEY hkHive,
41 __in_z LPCWSTR wzProviderKey,
42 __in_z_opt LPCWSTR wzMinVersion,
43 __in_z_opt LPCWSTR wzMaxVersion,
44 __in int iAttributes,
45 __in STRINGDICT_HANDLE sdDependencies,
46 __deref_inout_ecount_opt(*pcDependencies) DEPENDENCY** prgDependencies,
47 __inout LPUINT pcDependencies
48 );
49
50/***************************************************************************
51 DepCheckDependents - Checks if any dependents are still installed for the
52 given provider key.
53
54***************************************************************************/
55DAPI_(HRESULT) DepCheckDependents(
56 __in HKEY hkHive,
57 __in_z LPCWSTR wzProviderKey,
58 __reserved int iAttributes,
59 __in C_STRINGDICT_HANDLE sdIgnoredDependents,
60 __deref_inout_ecount_opt(*pcDependents) DEPENDENCY** prgDependents,
61 __inout LPUINT pcDependents
62 );
63
64/***************************************************************************
65 DepRegisterDependency - Registers the dependency provider.
66
67***************************************************************************/
68DAPI_(HRESULT) DepRegisterDependency(
69 __in HKEY hkHive,
70 __in_z LPCWSTR wzProviderKey,
71 __in_z LPCWSTR wzVersion,
72 __in_z LPCWSTR wzDisplayName,
73 __in_z_opt LPCWSTR wzId,
74 __in int iAttributes
75 );
76
77/***************************************************************************
78 DepDependentExists - Determines if a dependent is registered.
79
80 Note: Returns S_OK if dependent is registered.
81 Returns E_FILENOTFOUND if dependent is not registered
82***************************************************************************/
83DAPI_(HRESULT) DepDependentExists(
84 __in HKEY hkHive,
85 __in_z LPCWSTR wzDependencyProviderKey,
86 __in_z LPCWSTR wzProviderKey
87 );
88
89/***************************************************************************
90 DepRegisterDependent - Registers a dependent under the dependency provider.
91
92***************************************************************************/
93DAPI_(HRESULT) DepRegisterDependent(
94 __in HKEY hkHive,
95 __in_z LPCWSTR wzDependencyProviderKey,
96 __in_z LPCWSTR wzProviderKey,
97 __in_z_opt LPCWSTR wzMinVersion,
98 __in_z_opt LPCWSTR wzMaxVersion,
99 __in int iAttributes
100 );
101
102/***************************************************************************
103 DepUnregisterDependency - Removes the dependency provider.
104
105 Note: Caller should call CheckDependents prior to remove a dependency.
106 Returns E_FILENOTFOUND if the dependency is not registered.
107***************************************************************************/
108DAPI_(HRESULT) DepUnregisterDependency(
109 __in HKEY hkHive,
110 __in_z LPCWSTR wzProviderKey
111 );
112
113/***************************************************************************
114 DepUnregisterDependent - Removes a dependent under the dependency provider.
115
116 Note: Returns E_FILENOTFOUND if neither the dependency or dependent are
117 registered.
118 ***************************************************************************/
119DAPI_(HRESULT) DepUnregisterDependent(
120 __in HKEY hkHive,
121 __in_z LPCWSTR wzDependencyProviderKey,
122 __in_z LPCWSTR wzProviderKey
123 );
124
125/***************************************************************************
126 DependencyArrayAlloc - Allocates or expands an array of DEPENDENCY structs.
127
128***************************************************************************/
129DAPI_(HRESULT) DepDependencyArrayAlloc(
130 __deref_inout_ecount_opt(*pcDependencies) DEPENDENCY** prgDependencies,
131 __inout LPUINT pcDependencies,
132 __in_z LPCWSTR wzKey,
133 __in_z_opt LPCWSTR wzName
134 );
135
136/***************************************************************************
137 DepDependencyArrayFree - Frees an array of DEPENDENCY structs.
138
139***************************************************************************/
140DAPI_(void) DepDependencyArrayFree(
141 __in_ecount(cDependencies) DEPENDENCY* rgDependencies,
142 __in UINT cDependencies
143 );
144
145#ifdef __cplusplus
146}
147#endif
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/dictutil.h b/src/libs/dutil/WixToolset.DUtil/inc/dictutil.h
new file mode 100644
index 00000000..f0a3bb5a
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/dictutil.h
@@ -0,0 +1,69 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9#define ReleaseDict(sdh) if (sdh) { DictDestroy(sdh); }
10#define ReleaseNullDict(sdh) if (sdh) { DictDestroy(sdh); sdh = NULL; }
11
12typedef void* STRINGDICT_HANDLE;
13typedef const void* C_STRINGDICT_HANDLE;
14
15extern const int STRINGDICT_HANDLE_BYTES;
16
17typedef enum DICT_FLAG
18{
19 DICT_FLAG_NONE = 0,
20 DICT_FLAG_CASEINSENSITIVE = 1
21} DICT_FLAG;
22
23HRESULT DAPI DictCreateWithEmbeddedKey(
24 __out_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE* psdHandle,
25 __in DWORD dwNumExpectedItems,
26 __in_opt void **ppvArray,
27 __in size_t cByteOffset,
28 __in DICT_FLAG dfFlags
29 );
30HRESULT DAPI DictCreateStringList(
31 __out_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE* psdHandle,
32 __in DWORD dwNumExpectedItems,
33 __in DICT_FLAG dfFlags
34 );
35HRESULT DAPI DictCreateStringListFromArray(
36 __out_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE* psdHandle,
37 __in_ecount(cStringArray) const LPCWSTR* rgwzStringArray,
38 __in const DWORD cStringArray,
39 __in DICT_FLAG dfFlags
40 );
41HRESULT DAPI DictCompareStringListToArray(
42 __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdStringList,
43 __in_ecount(cStringArray) const LPCWSTR* rgwzStringArray,
44 __in const DWORD cStringArray
45 );
46HRESULT DAPI DictAddKey(
47 __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdHandle,
48 __in_z LPCWSTR szString
49 );
50HRESULT DAPI DictAddValue(
51 __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdHandle,
52 __in void *pvValue
53 );
54HRESULT DAPI DictKeyExists(
55 __in_bcount(STRINGDICT_HANDLE_BYTES) C_STRINGDICT_HANDLE sdHandle,
56 __in_z LPCWSTR szString
57 );
58HRESULT DAPI DictGetValue(
59 __in_bcount(STRINGDICT_HANDLE_BYTES) C_STRINGDICT_HANDLE sdHandle,
60 __in_z LPCWSTR szString,
61 __out void **ppvValue
62 );
63void DAPI DictDestroy(
64 __in_bcount(STRINGDICT_HANDLE_BYTES) STRINGDICT_HANDLE sdHandle
65 );
66
67#ifdef __cplusplus
68}
69#endif
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/dirutil.h b/src/libs/dutil/WixToolset.DUtil/inc/dirutil.h
new file mode 100644
index 00000000..539b3a73
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/dirutil.h
@@ -0,0 +1,59 @@
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
5typedef enum DIR_DELETE
6{
7 DIR_DELETE_FILES = 1,
8 DIR_DELETE_RECURSE = 2,
9 DIR_DELETE_SCHEDULE = 4,
10} DIR_DELETE;
11
12#ifdef __cplusplus
13extern "C" {
14#endif
15
16BOOL DAPI DirExists(
17 __in_z LPCWSTR wzPath,
18 __out_opt DWORD *pdwAttributes
19 );
20
21HRESULT DAPI DirCreateTempPath(
22 __in_z LPCWSTR wzPrefix,
23 __out_ecount_z(cchPath) LPWSTR wzPath,
24 __in DWORD cchPath
25 );
26
27HRESULT DAPI DirEnsureExists(
28 __in_z LPCWSTR wzPath,
29 __in_opt LPSECURITY_ATTRIBUTES psa
30 );
31
32HRESULT DAPI DirEnsureDelete(
33 __in_z LPCWSTR wzPath,
34 __in BOOL fDeleteFiles,
35 __in BOOL fRecurse
36 );
37
38HRESULT DAPI DirEnsureDeleteEx(
39 __in_z LPCWSTR wzPath,
40 __in DWORD dwFlags
41 );
42
43DWORD DAPI DirDeleteEmptyDirectoriesToRoot(
44 __in_z LPCWSTR wzPath,
45 __in DWORD dwFlags
46 );
47
48HRESULT DAPI DirGetCurrent(
49 __deref_out_z LPWSTR* psczCurrentDirectory
50 );
51
52HRESULT DAPI DirSetCurrent(
53 __in_z LPCWSTR wzDirectory
54 );
55
56#ifdef __cplusplus
57}
58#endif
59
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/dlutil.h b/src/libs/dutil/WixToolset.DUtil/inc/dlutil.h
new file mode 100644
index 00000000..3e95103a
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/dlutil.h
@@ -0,0 +1,59 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9typedef HRESULT (WINAPI *LPAUTHENTICATION_ROUTINE)(
10 __in LPVOID pVoid,
11 __in HINTERNET hUrl,
12 __in long lHttpCode,
13 __out BOOL* pfRetrySend,
14 __out BOOL* pfRetry
15 );
16
17typedef int (WINAPI *LPCANCEL_ROUTINE)(
18 __in HRESULT hrError,
19 __in_z_opt LPCWSTR wzError,
20 __in BOOL fAllowRetry,
21 __in_opt LPVOID pvContext
22 );
23
24// structs
25typedef struct _DOWNLOAD_SOURCE
26{
27 LPWSTR sczUrl;
28 LPWSTR sczUser;
29 LPWSTR sczPassword;
30} DOWNLOAD_SOURCE;
31
32typedef struct _DOWNLOAD_CACHE_CALLBACK
33{
34 LPPROGRESS_ROUTINE pfnProgress;
35 LPCANCEL_ROUTINE pfnCancel;
36 LPVOID pv;
37} DOWNLOAD_CACHE_CALLBACK;
38
39typedef struct _DOWNLOAD_AUTHENTICATION_CALLBACK
40{
41 LPAUTHENTICATION_ROUTINE pfnAuthenticate;
42 LPVOID pv;
43} DOWNLOAD_AUTHENTICATION_CALLBACK;
44
45
46// functions
47
48HRESULT DAPI DownloadUrl(
49 __in DOWNLOAD_SOURCE* pDownloadSource,
50 __in DWORD64 dw64AuthoredDownloadSize,
51 __in LPCWSTR wzDestinationPath,
52 __in_opt DOWNLOAD_CACHE_CALLBACK* pCache,
53 __in_opt DOWNLOAD_AUTHENTICATION_CALLBACK* pAuthenticate
54 );
55
56
57#ifdef __cplusplus
58}
59#endif
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/dpiutil.h b/src/libs/dutil/WixToolset.DUtil/inc/dpiutil.h
new file mode 100644
index 00000000..b30e2332
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/dpiutil.h
@@ -0,0 +1,120 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9// from WinUser.h
10#ifndef WM_DPICHANGED
11#define WM_DPICHANGED 0x02E0
12#endif
13#ifndef USER_DEFAULT_SCREEN_DPI
14#define USER_DEFAULT_SCREEN_DPI 96
15#endif
16
17typedef enum DPIU_AWARENESS
18{
19 DPIU_AWARENESS_NONE = 0x0,
20 DPIU_AWARENESS_SYSTEM = 0x1,
21 DPIU_AWARENESS_PERMONITOR = 0x2,
22 DPIU_AWARENESS_PERMONITORV2 = 0x4,
23 DPIU_AWARENESS_GDISCALED = 0x8,
24} DPIU_PROCESS_AWARENESS;
25
26typedef struct _DPIU_MONITOR_CONTEXT
27{
28 UINT nDpi;
29 MONITORINFOEXW mi;
30} DPIU_MONITOR_CONTEXT;
31
32typedef struct _DPIU_WINDOW_CONTEXT
33{
34 UINT nDpi;
35} DPIU_WINDOW_CONTEXT;
36
37typedef BOOL (APIENTRY* PFN_ADJUSTWINDOWRECTEXFORDPI)(
38 __in LPRECT lpRect,
39 __in DWORD dwStyle,
40 __in BOOL bMenu,
41 __in DWORD dwExStyle,
42 __in UINT dpi
43 );
44typedef UINT (APIENTRY *PFN_GETDPIFORWINDOW)(
45 __in HWND hwnd
46 );
47typedef BOOL (APIENTRY* PFN_SETPROCESSDPIAWARE)();
48typedef BOOL (APIENTRY* PFN_SETPROCESSDPIAWARENESSCONTEXT)(
49 __in DPI_AWARENESS_CONTEXT value
50 );
51
52#ifdef DPI_ENUMS_DECLARED
53typedef HRESULT(APIENTRY* PFN_GETDPIFORMONITOR)(
54 __in HMONITOR hmonitor,
55 __in MONITOR_DPI_TYPE dpiType,
56 __in UINT* dpiX,
57 __in UINT* dpiY
58 );
59typedef HRESULT(APIENTRY* PFN_SETPROCESSDPIAWARENESS)(
60 __in PROCESS_DPI_AWARENESS value
61 );
62#endif
63
64void DAPI DpiuInitialize();
65void DAPI DpiuUninitialize();
66
67/********************************************************************
68 DpiuAdjustWindowRect - calculate the required size of the window rectangle,
69 based on the desired size of the client rectangle
70 and the provided DPI.
71
72*******************************************************************/
73void DAPI DpiuAdjustWindowRect(
74 __in RECT* pWindowRect,
75 __in DWORD dwStyle,
76 __in BOOL fMenu,
77 __in DWORD dwExStyle,
78 __in UINT nDpi
79 );
80
81/********************************************************************
82 DpiuGetMonitorContextFromPoint - get the DPI context of the monitor from the given point.
83
84*******************************************************************/
85HRESULT DAPI DpiuGetMonitorContextFromPoint(
86 __in const POINT* pt,
87 __out DPIU_MONITOR_CONTEXT** ppMonitorContext
88 );
89
90/********************************************************************
91 DpiuGetWindowContext - get the DPI context of the given window.
92
93*******************************************************************/
94void DAPI DpiuGetWindowContext(
95 __in HWND hWnd,
96 __in DPIU_WINDOW_CONTEXT* pWindowContext
97 );
98
99/********************************************************************
100 DpiuScaleValue - scale the value to the target DPI.
101
102*******************************************************************/
103int DAPI DpiuScaleValue(
104 __in int nDefaultDpiValue,
105 __in UINT nTargetDpi
106 );
107
108/********************************************************************
109 DpiuSetProcessDpiAwareness - set the process DPI awareness. The ranking is
110 PERMONITORV2 > PERMONITOR > SYSTEM > GDISCALED > NONE.
111
112*******************************************************************/
113HRESULT DAPI DpiuSetProcessDpiAwareness(
114 __in DPIU_AWARENESS supportedAwareness,
115 __in_opt DPIU_AWARENESS* pSelectedAwareness
116 );
117
118#ifdef __cplusplus
119}
120#endif
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/dutil.h b/src/libs/dutil/WixToolset.DUtil/inc/dutil.h
new file mode 100644
index 00000000..fc9ec0f4
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/dutil.h
@@ -0,0 +1,190 @@
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#include "dutilsources.h"
5
6#define DAPI __stdcall
7#define DAPIV __cdecl // used only for functions taking variable length arguments
8
9#define DAPI_(type) EXTERN_C type DAPI
10#define DAPIV_(type) EXTERN_C type DAPIV
11
12
13// asserts and traces
14typedef BOOL (DAPI *DUTIL_ASSERTDISPLAYFUNCTION)(__in_z LPCSTR sz);
15
16typedef void (CALLBACK *DUTIL_CALLBACK_TRACEERROR)(
17 __in_z LPCSTR szFile,
18 __in int iLine,
19 __in REPORT_LEVEL rl,
20 __in UINT source,
21 __in HRESULT hrError,
22 __in_z __format_string LPCSTR szFormat,
23 __in va_list args
24 );
25
26#ifdef __cplusplus
27extern "C" {
28#endif
29
30/********************************************************************
31 DutilInitialize - initialize dutil.
32
33*******************************************************************/
34HRESULT DAPI DutilInitialize(
35 __in_opt DUTIL_CALLBACK_TRACEERROR pfnTraceErrorCallback
36 );
37
38/********************************************************************
39 DutilUninitialize - uninitialize dutil.
40
41*******************************************************************/
42void DAPI DutilUninitialize();
43
44void DAPI Dutil_SetAssertModule(__in HMODULE hAssertModule);
45void DAPI Dutil_SetAssertDisplayFunction(__in DUTIL_ASSERTDISPLAYFUNCTION pfn);
46void DAPI Dutil_Assert(__in_z LPCSTR szFile, __in int iLine);
47void DAPI Dutil_AssertSz(__in_z LPCSTR szFile, __in int iLine, __in_z __format_string LPCSTR szMessage);
48
49void DAPI Dutil_TraceSetLevel(__in REPORT_LEVEL ll, __in BOOL fTraceFilenames);
50REPORT_LEVEL DAPI Dutil_TraceGetLevel();
51void DAPIV Dutil_Trace(__in_z LPCSTR szFile, __in int iLine, __in REPORT_LEVEL rl, __in_z __format_string LPCSTR szMessage, ...);
52void DAPIV Dutil_TraceError(__in_z LPCSTR szFile, __in int iLine, __in REPORT_LEVEL rl, __in HRESULT hr, __in_z __format_string LPCSTR szMessage, ...);
53void DAPIV Dutil_TraceErrorSource(__in_z LPCSTR szFile, __in int iLine, __in REPORT_LEVEL rl, __in UINT source, __in HRESULT hr, __in_z __format_string LPCSTR szMessage, ...);
54void DAPI Dutil_RootFailure(__in_z LPCSTR szFile, __in int iLine, __in HRESULT hrError);
55
56#ifdef __cplusplus
57}
58#endif
59
60
61#ifdef DEBUG
62
63#define AssertSetModule(m) (void)Dutil_SetAssertModule(m)
64#define AssertSetDisplayFunction(pfn) (void)Dutil_SetAssertDisplayFunction(pfn)
65#define Assert(f) ((f) ? (void)0 : (void)Dutil_Assert(__FILE__, __LINE__))
66#define AssertSz(f, sz) ((f) ? (void)0 : (void)Dutil_AssertSz(__FILE__, __LINE__, sz))
67
68#define TraceSetLevel(l, f) (void)Dutil_TraceSetLevel(l, f)
69#define TraceGetLevel() (REPORT_LEVEL)Dutil_TraceGetLevel()
70#define Trace(l, f, ...) (void)Dutil_Trace(__FILE__, __LINE__, l, f, __VA_ARGS__)
71#define TraceError(x, f, ...) (void)Dutil_TraceError(__FILE__, __LINE__, REPORT_ERROR, x, f, __VA_ARGS__)
72#define TraceErrorDebug(x, f, ...) (void)Dutil_TraceError(__FILE__, __LINE__, REPORT_DEBUG, x, f, __VA_ARGS__)
73
74#else // !DEBUG
75
76#define AssertSetModule(m)
77#define AssertSetDisplayFunction(pfn)
78#define Assert(f)
79#define AssertSz(f, sz)
80
81#define TraceSetLevel(l, f)
82#define Trace(l, f, ...)
83#define TraceError(x, f, ...)
84#define TraceErrorDebug(x, f, ...)
85
86#endif // DEBUG
87
88// DUTIL_SOURCE_DEFAULT can be overriden
89#ifndef DUTIL_SOURCE_DEFAULT
90#define DUTIL_SOURCE_DEFAULT DUTIL_SOURCE_UNKNOWN
91#endif
92
93// Exit macros
94#define ExitFunction() { goto LExit; }
95#define ExitFunction1(x) { x; goto LExit; }
96
97#define ExitFunctionWithLastError(x) { x = HRESULT_FROM_WIN32(::GetLastError()); goto LExit; }
98
99#define ExitTraceSource(d, x, s, ...) { TraceError(x, s, __VA_ARGS__); (void)Dutil_TraceErrorSource(__FILE__, __LINE__, REPORT_ERROR, d, x, s, __VA_ARGS__); }
100#define ExitTraceDebugSource(d, x, s, ...) { TraceErrorDebug(x, s, __VA_ARGS__); (void)Dutil_TraceErrorSource(__FILE__, __LINE__, REPORT_DEBUG, d, x, s, __VA_ARGS__); }
101
102#define ExitOnLastErrorSource(d, x, s, ...) { DWORD Dutil_er = ::GetLastError(); x = HRESULT_FROM_WIN32(Dutil_er); if (FAILED(x)) { Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceSource(d, x, s, __VA_ARGS__); goto LExit; } }
103#define ExitOnLastErrorDebugTraceSource(d, x, s, ...) { DWORD Dutil_er = ::GetLastError(); x = HRESULT_FROM_WIN32(Dutil_er); if (FAILED(x)) { Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceDebugSource(d, x, s, __VA_ARGS__); goto LExit; } }
104#define ExitWithLastErrorSource(d, x, s, ...) { DWORD Dutil_er = ::GetLastError(); x = HRESULT_FROM_WIN32(Dutil_er); if (!FAILED(x)) { x = E_FAIL; } Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceSource(d, x, s, __VA_ARGS__); goto LExit; }
105#define ExitOnFailureSource(d, x, s, ...) if (FAILED(x)) { ExitTraceSource(d, x, s, __VA_ARGS__); goto LExit; }
106#define ExitOnRootFailureSource(d, x, s, ...) if (FAILED(x)) { Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceSource(d, x, s, __VA_ARGS__); goto LExit; }
107#define ExitOnFailureDebugTraceSource(d, x, s, ...) if (FAILED(x)) { ExitTraceDebugSource(d, x, s, __VA_ARGS__); goto LExit; }
108#define ExitOnNullSource(d, p, x, e, s, ...) if (NULL == p) { x = e; Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceSource(d, x, s, __VA_ARGS__); goto LExit; }
109#define ExitOnNullWithLastErrorSource(d, p, x, s, ...) if (NULL == p) { DWORD Dutil_er = ::GetLastError(); x = HRESULT_FROM_WIN32(Dutil_er); if (!FAILED(x)) { x = E_FAIL; } Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceSource(d, x, s, __VA_ARGS__); goto LExit; }
110#define ExitOnNullDebugTraceSource(d, p, x, e, s, ...) if (NULL == p) { x = e; Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceDebugSource(d, x, s, __VA_ARGS__); goto LExit; }
111#define ExitOnInvalidHandleWithLastErrorSource(d, p, x, s, ...) if (INVALID_HANDLE_VALUE == p) { DWORD Dutil_er = ::GetLastError(); x = HRESULT_FROM_WIN32(Dutil_er); if (!FAILED(x)) { x = E_FAIL; } Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceSource(d, x, s, __VA_ARGS__); goto LExit; }
112#define ExitOnWin32ErrorSource(d, e, x, s, ...) if (ERROR_SUCCESS != e) { x = HRESULT_FROM_WIN32(e); if (!FAILED(x)) { x = E_FAIL; } Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceSource(d, x, s, __VA_ARGS__); goto LExit; }
113
114#define ExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_DEFAULT, x, s, __VA_ARGS__)
115#define ExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_DEFAULT, x, s, __VA_ARGS__)
116#define ExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_DEFAULT, x, s, __VA_ARGS__)
117#define ExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_DEFAULT, x, s, __VA_ARGS__)
118#define ExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_DEFAULT, x, s, __VA_ARGS__)
119#define ExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_DEFAULT, x, s, __VA_ARGS__)
120#define ExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_DEFAULT, p, x, e, s, __VA_ARGS__)
121#define ExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_DEFAULT, p, x, s, __VA_ARGS__)
122#define ExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_DEFAULT, p, x, e, s, __VA_ARGS__)
123#define ExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_DEFAULT, p, x, s, __VA_ARGS__)
124#define ExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_DEFAULT, e, x, s, __VA_ARGS__)
125
126// release macros
127#define ReleaseObject(x) if (x) { x->Release(); }
128#define ReleaseObjectArray(prg, cel) if (prg) { for (DWORD Dutil_ReleaseObjectArrayIndex = 0; Dutil_ReleaseObjectArrayIndex < cel; ++Dutil_ReleaseObjectArrayIndex) { ReleaseObject(prg[Dutil_ReleaseObjectArrayIndex]); } ReleaseMem(prg); }
129#define ReleaseVariant(x) { ::VariantClear(&x); }
130#define ReleaseNullObject(x) if (x) { (x)->Release(); x = NULL; }
131#define ReleaseCertificate(x) if (x) { ::CertFreeCertificateContext(x); x=NULL; }
132#define ReleaseHandle(x) if (x) { ::CloseHandle(x); x = NULL; }
133
134
135// useful defines and macros
136#define Unused(x) ((void)x)
137
138#ifndef countof
139#if 1
140#define countof(ary) (sizeof(ary) / sizeof(ary[0]))
141#else
142#ifndef __cplusplus
143#define countof(ary) (sizeof(ary) / sizeof(ary[0]))
144#else
145template<typename T> static char countofVerify(void const *, T) throw() { return 0; }
146template<typename T> static void countofVerify(T *const, T *const *) throw() {};
147#define countof(arr) (sizeof(countofVerify(arr,&(arr))) * sizeof(arr)/sizeof(*(arr)))
148#endif
149#endif
150#endif
151
152#define roundup(x, n) roundup_typed(x, n, DWORD)
153#define roundup_typed(x, n, t) (((t)(x) + ((t)(n) - 1)) & ~((t)(n) - 1))
154
155#define HRESULT_FROM_RPC(x) ((HRESULT) ((x) | FACILITY_RPC))
156
157#ifndef MAXSIZE_T
158#define MAXSIZE_T ((SIZE_T)~((SIZE_T)0))
159#endif
160
161typedef const BYTE* LPCBYTE;
162
163#define E_FILENOTFOUND HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)
164#define E_PATHNOTFOUND HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND)
165#define E_INVALIDDATA HRESULT_FROM_WIN32(ERROR_INVALID_DATA)
166#define E_INVALIDSTATE HRESULT_FROM_WIN32(ERROR_INVALID_STATE)
167#define E_INSUFFICIENT_BUFFER HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)
168#define E_MOREDATA HRESULT_FROM_WIN32(ERROR_MORE_DATA)
169#define E_NOMOREITEMS HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS)
170#define E_NOTFOUND HRESULT_FROM_WIN32(ERROR_NOT_FOUND)
171#define E_MODNOTFOUND HRESULT_FROM_WIN32(ERROR_MOD_NOT_FOUND)
172#define E_BADCONFIGURATION HRESULT_FROM_WIN32(ERROR_BAD_CONFIGURATION)
173
174#define AddRefAndRelease(x) { x->AddRef(); x->Release(); }
175
176#define MAKEDWORD(lo, hi) ((DWORD)MAKELONG(lo, hi))
177#define MAKEQWORDVERSION(mj, mi, b, r) (((DWORD64)MAKELONG(r, b)) | (((DWORD64)MAKELONG(mi, mj)) << 32))
178
179
180#ifdef __cplusplus
181extern "C" {
182#endif
183
184// other functions
185HRESULT DAPI LoadSystemLibrary(__in_z LPCWSTR wzModuleName, __out HMODULE *phModule);
186HRESULT DAPI LoadSystemLibraryWithPath(__in_z LPCWSTR wzModuleName, __out HMODULE *phModule, __deref_out_z_opt LPWSTR* psczPath);
187
188#ifdef __cplusplus
189}
190#endif
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/dutilsources.h b/src/libs/dutil/WixToolset.DUtil/inc/dutilsources.h
new file mode 100644
index 00000000..7d512cb3
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/dutilsources.h
@@ -0,0 +1,76 @@
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
4typedef enum DUTIL_SOURCE
5{
6 DUTIL_SOURCE_UNKNOWN,
7 DUTIL_SOURCE_ACLUTIL,
8 DUTIL_SOURCE_APPUTIL,
9 DUTIL_SOURCE_APUPUTIL,
10 DUTIL_SOURCE_ATOMUTIL,
11 DUTIL_SOURCE_BUFFUTIL,
12 DUTIL_SOURCE_BUTIL,
13 DUTIL_SOURCE_CABCUTIL,
14 DUTIL_SOURCE_CABUTIL,
15 DUTIL_SOURCE_CERTUTIL,
16 DUTIL_SOURCE_CONUTIL,
17 DUTIL_SOURCE_CRYPUTIL,
18 DUTIL_SOURCE_DEPUTIL,
19 DUTIL_SOURCE_DICTUTIL,
20 DUTIL_SOURCE_DIRUTIL,
21 DUTIL_SOURCE_DLUTIL,
22 DUTIL_SOURCE_DPIUTIL,
23 DUTIL_SOURCE_DUTIL,
24 DUTIL_SOURCE_ESEUTIL,
25 DUTIL_SOURCE_FILEUTIL,
26 DUTIL_SOURCE_GDIPUTIL,
27 DUTIL_SOURCE_GUIDUTIL,
28 DUTIL_SOURCE_IIS7UTIL,
29 DUTIL_SOURCE_INETUTIL,
30 DUTIL_SOURCE_INIUTIL,
31 DUTIL_SOURCE_JSONUTIL,
32 DUTIL_SOURCE_LOCUTIL,
33 DUTIL_SOURCE_LOGUTIL,
34 DUTIL_SOURCE_MEMUTIL,
35 DUTIL_SOURCE_METAUTIL,
36 DUTIL_SOURCE_MONUTIL,
37 DUTIL_SOURCE_OSUTIL,
38 DUTIL_SOURCE_PATHUTIL,
39 DUTIL_SOURCE_PERFUTIL,
40 DUTIL_SOURCE_POLCUTIL,
41 DUTIL_SOURCE_PROCUTIL,
42 DUTIL_SOURCE_REGUTIL,
43 DUTIL_SOURCE_RESRUTIL,
44 DUTIL_SOURCE_RESWUTIL,
45 DUTIL_SOURCE_REXUTIL,
46 DUTIL_SOURCE_RMUTIL,
47 DUTIL_SOURCE_RSSUTIL,
48 DUTIL_SOURCE_SCEUTIL,
49 DUTIL_SOURCE_SCZUTIL,
50 DUTIL_SOURCE_SHELUTIL,
51 DUTIL_SOURCE_SQLUTIL,
52 DUTIL_SOURCE_SRPUTIL,
53 DUTIL_SOURCE_STRUTIL,
54 DUTIL_SOURCE_SVCUTIL,
55 DUTIL_SOURCE_THMUTIL,
56 DUTIL_SOURCE_TIMEUTIL,
57 DUTIL_SOURCE_UNCUTIL,
58 DUTIL_SOURCE_URIUTIL,
59 DUTIL_SOURCE_USERUTIL,
60 DUTIL_SOURCE_WIUTIL,
61 DUTIL_SOURCE_WUAUTIL,
62 DUTIL_SOURCE_XMLUTIL,
63 DUTIL_SOURCE_VERUTIL,
64
65 DUTIL_SOURCE_EXTERNAL = 256,
66} DUTIL_SOURCE;
67
68typedef enum REPORT_LEVEL
69{
70 REPORT_NONE, // turns off report (only valid for XXXSetLevel())
71 REPORT_WARNING, // written if want only warnings or reporting is on in general
72 REPORT_STANDARD, // written if reporting is on
73 REPORT_VERBOSE, // written only if verbose reporting is on
74 REPORT_DEBUG, // reporting useful when debugging code
75 REPORT_ERROR, // always gets reported, but can never be specified
76} REPORT_LEVEL;
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/eseutil.h b/src/libs/dutil/WixToolset.DUtil/inc/eseutil.h
new file mode 100644
index 00000000..bea47b2b
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/eseutil.h
@@ -0,0 +1,223 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9#define ReleaseEseQuery(pqh) if (pqh) { EseFinishQuery(pqh); }
10#define ReleaseNullEseQuery(pqh) if (pqh) { EseFinishQuery(pqh); pqh = NULL; }
11
12struct ESE_COLUMN_SCHEMA
13{
14 JET_COLUMNID jcColumn;
15 LPCWSTR pszName;
16 JET_COLTYP jcColumnType;
17 BOOL fKey; // If this column is part of the key of the table
18 BOOL fFixed;
19 BOOL fNullable;
20 BOOL fAutoIncrement;
21};
22
23struct ESE_TABLE_SCHEMA
24{
25 JET_TABLEID jtTable;
26 LPCWSTR pszName;
27 DWORD dwColumns;
28 ESE_COLUMN_SCHEMA *pcsColumns;
29};
30
31struct ESE_DATABASE_SCHEMA
32{
33 DWORD dwTables;
34 ESE_TABLE_SCHEMA *ptsTables;
35};
36
37typedef enum ESE_QUERY_TYPE
38{
39 ESE_QUERY_EXACT,
40 ESE_QUERY_FROM_TOP,
41 ESE_QUERY_FROM_BOTTOM
42} ESE_QUERY_TYPE;
43
44typedef void* ESE_QUERY_HANDLE;
45
46HRESULT DAPI EseBeginSession(
47 __out JET_INSTANCE *pjiInstance,
48 __out JET_SESID *pjsSession,
49 __in_z LPCWSTR pszInstance,
50 __in_z LPCWSTR pszPath
51 );
52HRESULT DAPI EseEndSession(
53 __in JET_INSTANCE jiInstance,
54 __in JET_SESID jsSession
55 );
56HRESULT DAPI EseEnsureDatabase(
57 __in JET_SESID jsSession,
58 __in_z LPCWSTR pszFile,
59 __in ESE_DATABASE_SCHEMA *pdsSchema,
60 __out JET_DBID* pjdbDb,
61 __in BOOL fExclusive,
62 __in BOOL fReadonly
63 );
64HRESULT DAPI EseCloseDatabase(
65 __in JET_SESID jsSession,
66 __in JET_DBID jdbDb
67 );
68HRESULT DAPI EseCreateTable(
69 __in JET_SESID jsSession,
70 __in JET_DBID jdbDb,
71 __in_z LPCWSTR pszTable,
72 __out JET_TABLEID *pjtTable
73 );
74HRESULT DAPI EseOpenTable(
75 __in JET_SESID jsSession,
76 __in JET_DBID jdbDb,
77 __in_z LPCWSTR pszTable,
78 __out JET_TABLEID *pjtTable
79 );
80HRESULT DAPI EseCloseTable(
81 __in JET_SESID jsSession,
82 __in JET_TABLEID jtTable
83 );
84HRESULT DAPI EseEnsureColumn(
85 __in JET_SESID jsSession,
86 __in JET_TABLEID jtTable,
87 __in_z LPCWSTR pszColumnName,
88 __in JET_COLTYP jcColumnType,
89 __in ULONG ulColumnSize,
90 __in BOOL fFixed,
91 __in BOOL fNullable,
92 __out_opt JET_COLUMNID *pjcColumn
93 );
94HRESULT DAPI EseGetColumn(
95 __in JET_SESID jsSession,
96 __in JET_TABLEID jtTable,
97 __in_z LPCWSTR pszColumnName,
98 __out JET_COLUMNID *pjcColumn
99 );
100HRESULT DAPI EseMoveCursor(
101 __in JET_SESID jsSession,
102 __in JET_TABLEID jtTable,
103 __in LONG lRow
104 );
105HRESULT DAPI EseDeleteRow(
106 __in JET_SESID jsSession,
107 __in JET_TABLEID jtTable
108 );
109HRESULT DAPI EseBeginTransaction(
110 __in JET_SESID jsSession
111 );
112HRESULT DAPI EseRollbackTransaction(
113 __in JET_SESID jsSession,
114 __in BOOL fAll
115 );
116HRESULT DAPI EseCommitTransaction(
117 __in JET_SESID jsSession
118 );
119HRESULT DAPI EsePrepareUpdate(
120 __in JET_SESID jsSession,
121 __in JET_TABLEID jtTable,
122 __in ULONG ulPrep
123 );
124HRESULT DAPI EseFinishUpdate(
125 __in JET_SESID jsSession,
126 __in JET_TABLEID jtTable,
127 __in BOOL fSeekToInsertedRecord
128 );
129HRESULT DAPI EseSetColumnBinary(
130 __in JET_SESID jsSession,
131 __in ESE_TABLE_SCHEMA tsTable,
132 __in DWORD dwColumn,
133 __in_bcount(cbBuffer) const BYTE* pbBuffer,
134 __in SIZE_T cbBuffer
135 );
136HRESULT DAPI EseSetColumnDword(
137 __in JET_SESID jsSession,
138 __in ESE_TABLE_SCHEMA tsTable,
139 __in DWORD dwColumn,
140 __in DWORD dwValue
141 );
142HRESULT DAPI EseSetColumnBool(
143 __in JET_SESID jsSession,
144 __in ESE_TABLE_SCHEMA tsTable,
145 __in DWORD dwColumn,
146 __in BOOL fValue
147 );
148HRESULT DAPI EseSetColumnString(
149 __in JET_SESID jsSession,
150 __in ESE_TABLE_SCHEMA tsTable,
151 __in DWORD dwColumn,
152 __in_z LPCWSTR pszValue
153 );
154HRESULT DAPI EseSetColumnEmpty(
155 __in JET_SESID jsSession,
156 __in ESE_TABLE_SCHEMA tsTable,
157 __in DWORD dwColumn
158 );
159HRESULT DAPI EseGetColumnBinary(
160 __in JET_SESID jsSession,
161 __in ESE_TABLE_SCHEMA tsTable,
162 __in DWORD dwColumn,
163 __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer,
164 __inout SIZE_T* piBuffer
165 );
166HRESULT DAPI EseGetColumnDword(
167 __in JET_SESID jsSession,
168 __in ESE_TABLE_SCHEMA tsTable,
169 __in DWORD dwColumn,
170 __out DWORD *pdwValue
171 );
172HRESULT DAPI EseGetColumnBool(
173 __in JET_SESID jsSession,
174 __in ESE_TABLE_SCHEMA tsTable,
175 __in DWORD dwColumn,
176 __out BOOL *pfValue
177 );
178HRESULT DAPI EseGetColumnString(
179 __in JET_SESID jsSession,
180 __in ESE_TABLE_SCHEMA tsTable,
181 __in DWORD dwColumn,
182 __out LPWSTR *ppszValue
183 );
184
185// Call this once for each key column in the table
186HRESULT DAPI EseBeginQuery(
187 __in JET_SESID jsSession,
188 __in JET_TABLEID jtTable,
189 __in ESE_QUERY_TYPE qtQueryType,
190 __out ESE_QUERY_HANDLE *peqhHandle
191 );
192HRESULT DAPI EseSetQueryColumnBinary(
193 __in ESE_QUERY_HANDLE eqhHandle,
194 __in_bcount(cbBuffer) const BYTE* pbBuffer,
195 __in SIZE_T cbBuffer,
196 __in BOOL fFinal // If this is true, all other key columns in the query will be set to "*"
197 );
198HRESULT DAPI EseSetQueryColumnDword(
199 __in ESE_QUERY_HANDLE eqhHandle,
200 __in DWORD dwData,
201 __in BOOL fFinal // If this is true, all other key columns in the query will be set to "*"
202 );
203HRESULT DAPI EseSetQueryColumnBool(
204 __in ESE_QUERY_HANDLE eqhHandle,
205 __in BOOL fValue,
206 __in BOOL fFinal // If this is true, all other key columns in the query will be set to "*"
207 );
208HRESULT DAPI EseSetQueryColumnString(
209 __in ESE_QUERY_HANDLE eqhHandle,
210 __in_z LPCWSTR pszString,
211 __in BOOL fFinal // If this is true, all other key columns in the query will be set to "*"
212 );
213HRESULT DAPI EseFinishQuery(
214 __in ESE_QUERY_HANDLE eqhHandle
215 );
216// Once all columns have been set up, call this and read the result
217HRESULT DAPI EseRunQuery(
218 __in ESE_QUERY_HANDLE eqhHandle
219 );
220
221#ifdef __cplusplus
222}
223#endif
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/fileutil.h b/src/libs/dutil/WixToolset.DUtil/inc/fileutil.h
new file mode 100644
index 00000000..d3e326f7
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/fileutil.h
@@ -0,0 +1,247 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9#define ReleaseFile(h) if (INVALID_HANDLE_VALUE != h) { ::CloseHandle(h); h = INVALID_HANDLE_VALUE; }
10#define ReleaseFileHandle(h) if (INVALID_HANDLE_VALUE != h) { ::CloseHandle(h); h = INVALID_HANDLE_VALUE; }
11#define ReleaseFileFindHandle(h) if (INVALID_HANDLE_VALUE != h) { ::FindClose(h); h = INVALID_HANDLE_VALUE; }
12
13#define FILEMAKEVERSION(major, minor, build, revision) static_cast<DWORD64>((static_cast<DWORD64>(major & 0xFFFF) << 48) \
14 | (static_cast<DWORD64>(minor & 0xFFFF) << 32) \
15 | (static_cast<DWORD64>(build & 0xFFFF) << 16) \
16 | (static_cast<DWORD64>(revision & 0xFFFF)))
17
18typedef enum FILE_ARCHITECTURE
19{
20 FILE_ARCHITECTURE_UNKNOWN,
21 FILE_ARCHITECTURE_X86,
22 FILE_ARCHITECTURE_X64,
23 FILE_ARCHITECTURE_IA64,
24} FILE_ARCHITECTURE;
25
26typedef enum FILE_ENCODING
27{
28 FILE_ENCODING_UNSPECIFIED = 0,
29 // TODO: distinguish between non-BOM utf-8 and ANSI in the future?
30 FILE_ENCODING_UTF8,
31 FILE_ENCODING_UTF8_WITH_BOM,
32 FILE_ENCODING_UTF16,
33 FILE_ENCODING_UTF16_WITH_BOM,
34} FILE_ENCODING;
35
36
37LPWSTR DAPI FileFromPath(
38 __in_z LPCWSTR wzPath
39 );
40HRESULT DAPI FileResolvePath(
41 __in_z LPCWSTR wzRelativePath,
42 __out LPWSTR *ppwzFullPath
43 );
44HRESULT DAPI FileStripExtension(
45 __in_z LPCWSTR wzFileName,
46 __out LPWSTR *ppwzFileNameNoExtension
47 );
48HRESULT DAPI FileChangeExtension(
49 __in_z LPCWSTR wzFileName,
50 __in_z LPCWSTR wzNewExtension,
51 __out LPWSTR *ppwzFileNameNewExtension
52 );
53HRESULT DAPI FileAddSuffixToBaseName(
54 __in_z LPCWSTR wzFileName,
55 __in_z LPCWSTR wzSuffix,
56 __out_z LPWSTR* psczNewFileName
57 );
58HRESULT DAPI FileVersionFromString(
59 __in_z LPCWSTR wzVersion,
60 __out DWORD *pdwVerMajor,
61 __out DWORD* pdwVerMinor
62 );
63HRESULT DAPI FileVersionFromStringEx(
64 __in_z LPCWSTR wzVersion,
65 __in SIZE_T cchVersion,
66 __out DWORD64* pqwVersion
67 );
68HRESULT DAPI FileVersionToStringEx(
69 __in DWORD64 qwVersion,
70 __out LPWSTR* psczVersion
71 );
72HRESULT DAPI FileSetPointer(
73 __in HANDLE hFile,
74 __in DWORD64 dw64Move,
75 __out_opt DWORD64* pdw64NewPosition,
76 __in DWORD dwMoveMethod
77 );
78HRESULT DAPI FileSize(
79 __in_z LPCWSTR pwzFileName,
80 __out LONGLONG* pllSize
81 );
82HRESULT DAPI FileSizeByHandle(
83 __in HANDLE hFile,
84 __out LONGLONG* pllSize
85 );
86BOOL DAPI FileExistsEx(
87 __in_z LPCWSTR wzPath,
88 __out_opt DWORD *pdwAttributes
89 );
90BOOL DAPI FileExistsAfterRestart(
91 __in_z LPCWSTR wzPath,
92 __out_opt DWORD *pdwAttributes
93 );
94HRESULT DAPI FileRemoveFromPendingRename(
95 __in_z LPCWSTR wzPath
96 );
97HRESULT DAPI FileRead(
98 __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest,
99 __out SIZE_T* pcbDest,
100 __in_z LPCWSTR wzSrcPath
101 );
102HRESULT DAPI FileReadEx(
103 __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest,
104 __out SIZE_T* pcbDest,
105 __in_z LPCWSTR wzSrcPath,
106 __in DWORD dwShareMode
107 );
108HRESULT DAPI FileReadUntil(
109 __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest,
110 __out_range(<=, cbMaxRead) SIZE_T* pcbDest,
111 __in_z LPCWSTR wzSrcPath,
112 __in DWORD cbMaxRead
113 );
114HRESULT DAPI FileReadPartial(
115 __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest,
116 __out_range(<=, cbMaxRead) SIZE_T* pcbDest,
117 __in_z LPCWSTR wzSrcPath,
118 __in BOOL fSeek,
119 __in DWORD cbStartPosition,
120 __in DWORD cbMaxRead,
121 __in BOOL fPartialOK
122 );
123HRESULT DAPI FileReadPartialEx(
124 __deref_inout_bcount_full(*pcbDest) LPBYTE* ppbDest,
125 __out_range(<=, cbMaxRead) SIZE_T* pcbDest,
126 __in_z LPCWSTR wzSrcPath,
127 __in BOOL fSeek,
128 __in DWORD cbStartPosition,
129 __in DWORD cbMaxRead,
130 __in BOOL fPartialOK,
131 __in DWORD dwShareMode
132 );
133HRESULT DAPI FileReadHandle(
134 __in HANDLE hFile,
135 __in_bcount(cbDest) LPBYTE pbDest,
136 __in SIZE_T cbDest
137 );
138HRESULT DAPI FileWrite(
139 __in_z LPCWSTR pwzFileName,
140 __in DWORD dwFlagsAndAttributes,
141 __in_bcount_opt(cbData) LPCBYTE pbData,
142 __in SIZE_T cbData,
143 __out_opt HANDLE* pHandle
144 );
145HRESULT DAPI FileWriteHandle(
146 __in HANDLE hFile,
147 __in_bcount_opt(cbData) LPCBYTE pbData,
148 __in SIZE_T cbData
149 );
150HRESULT DAPI FileCopyUsingHandles(
151 __in HANDLE hSource,
152 __in HANDLE hTarget,
153 __in DWORD64 cbCopy,
154 __out_opt DWORD64* pcbCopied
155 );
156HRESULT DAPI FileCopyUsingHandlesWithProgress(
157 __in HANDLE hSource,
158 __in HANDLE hTarget,
159 __in DWORD64 cbCopy,
160 __in_opt LPPROGRESS_ROUTINE lpProgressRoutine,
161 __in_opt LPVOID lpData
162 );
163HRESULT DAPI FileEnsureCopy(
164 __in_z LPCWSTR wzSource,
165 __in_z LPCWSTR wzTarget,
166 __in BOOL fOverwrite
167 );
168HRESULT DAPI FileEnsureCopyWithRetry(
169 __in LPCWSTR wzSource,
170 __in LPCWSTR wzTarget,
171 __in BOOL fOverwrite,
172 __in DWORD cRetry,
173 __in DWORD dwWaitMilliseconds
174 );
175HRESULT DAPI FileEnsureMove(
176 __in_z LPCWSTR wzSource,
177 __in_z LPCWSTR wzTarget,
178 __in BOOL fOverwrite,
179 __in BOOL fAllowCopy
180 );
181HRESULT DAPI FileEnsureMoveWithRetry(
182 __in LPCWSTR wzSource,
183 __in LPCWSTR wzTarget,
184 __in BOOL fOverwrite,
185 __in BOOL fAllowCopy,
186 __in DWORD cRetry,
187 __in DWORD dwWaitMilliseconds
188 );
189HRESULT DAPI FileCreateTemp(
190 __in_z LPCWSTR wzPrefix,
191 __in_z LPCWSTR wzExtension,
192 __deref_opt_out_z LPWSTR* ppwzTempFile,
193 __out_opt HANDLE* phTempFile
194 );
195HRESULT DAPI FileCreateTempW(
196 __in_z LPCWSTR wzPrefix,
197 __in_z LPCWSTR wzExtension,
198 __deref_opt_out_z LPWSTR* ppwzTempFile,
199 __out_opt HANDLE* phTempFile
200 );
201HRESULT DAPI FileVersion(
202 __in_z LPCWSTR wzFilename,
203 __out DWORD *pdwVerMajor,
204 __out DWORD* pdwVerMinor
205 );
206HRESULT DAPI FileIsSame(
207 __in_z LPCWSTR wzFile1,
208 __in_z LPCWSTR wzFile2,
209 __out LPBOOL lpfSameFile
210 );
211HRESULT DAPI FileEnsureDelete(
212 __in_z LPCWSTR wzFile
213 );
214HRESULT DAPI FileGetTime(
215 __in_z LPCWSTR wzFile,
216 __out_opt LPFILETIME lpCreationTime,
217 __out_opt LPFILETIME lpLastAccessTime,
218 __out_opt LPFILETIME lpLastWriteTime
219 );
220HRESULT DAPI FileSetTime(
221 __in_z LPCWSTR wzFile,
222 __in_opt const FILETIME *lpCreationTime,
223 __in_opt const FILETIME *lpLastAccessTime,
224 __in_opt const FILETIME *lpLastWriteTime
225 );
226HRESULT DAPI FileResetTime(
227 __in_z LPCWSTR wzFile
228 );
229HRESULT DAPI FileExecutableArchitecture(
230 __in_z LPCWSTR wzFile,
231 __out FILE_ARCHITECTURE *pArchitecture
232 );
233HRESULT DAPI FileToString(
234 __in_z LPCWSTR wzFile,
235 __out LPWSTR *psczString,
236 __out_opt FILE_ENCODING *pfeEncoding
237 );
238HRESULT DAPI FileFromString(
239 __in_z LPCWSTR wzFile,
240 __in DWORD dwFlagsAndAttributes,
241 __in_z LPCWSTR sczString,
242 __in FILE_ENCODING feEncoding
243 );
244
245#ifdef __cplusplus
246}
247#endif
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/gdiputil.h b/src/libs/dutil/WixToolset.DUtil/inc/gdiputil.h
new file mode 100644
index 00000000..f2145828
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/gdiputil.h
@@ -0,0 +1,39 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#define ExitOnGdipFailureSource(d, g, x, s, ...) { x = GdipHresultFromStatus(g); if (FAILED(x)) { Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceSource(d, x, s, __VA_ARGS__); goto LExit; } }
6#define ExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_DEFAULT, g, x, s, __VA_ARGS__)
7
8#ifdef __cplusplus
9extern "C" {
10#endif
11
12HRESULT DAPI GdipInitialize(
13 __in const Gdiplus::GdiplusStartupInput* pInput,
14 __out ULONG_PTR* pToken,
15 __out_opt Gdiplus::GdiplusStartupOutput *pOutput
16 );
17
18void DAPI GdipUninitialize(
19 __in ULONG_PTR token
20 );
21
22HRESULT DAPI GdipBitmapFromResource(
23 __in_opt HINSTANCE hinst,
24 __in_z LPCSTR szId,
25 __out Gdiplus::Bitmap **ppBitmap
26 );
27
28HRESULT DAPI GdipBitmapFromFile(
29 __in_z LPCWSTR wzFileName,
30 __out Gdiplus::Bitmap **ppBitmap
31 );
32
33HRESULT DAPI GdipHresultFromStatus(
34 __in Gdiplus::Status gs
35 );
36
37#ifdef __cplusplus
38}
39#endif
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/guidutil.h b/src/libs/dutil/WixToolset.DUtil/inc/guidutil.h
new file mode 100644
index 00000000..478a464f
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/guidutil.h
@@ -0,0 +1,21 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9#define GUID_STRING_LENGTH 39
10
11HRESULT DAPI GuidFixedCreate(
12 _Out_z_cap_c_(GUID_STRING_LENGTH) WCHAR* wzGuid
13 );
14
15HRESULT DAPI GuidCreate(
16 __deref_out_z LPWSTR* psczGuid
17 );
18
19#ifdef __cplusplus
20}
21#endif
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/iis7util.h b/src/libs/dutil/WixToolset.DUtil/inc/iis7util.h
new file mode 100644
index 00000000..3572b4e9
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/iis7util.h
@@ -0,0 +1,222 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9// IIS Config schema names
10#define IIS_CONFIG_ADD L"add"
11#define IIS_CONFIG_ALLOWED L"allowed"
12#define IIS_CONFIG_APPHOST_ROOT L"MACHINE/WEBROOT/APPHOST"
13#define IIS_CONFIG_APPLICATION L"application"
14#define IIS_CONFIG_APPPOOL L"applicationPool"
15#define IIS_CONFIG_APPPOOL_AUTO L"autoStart"
16#define IIS_CONFIG_APPPOOL_SECTION L"system.applicationHost/applicationPools"
17#define IIS_CONFIG_AUTOSTART L"serverAutoStart"
18#define IIS_CONFIG_BINDING L"binding"
19#define IIS_CONFIG_BINDINGINFO L"bindingInformation"
20#define IIS_CONFIG_BINDINGS L"bindings"
21#define IIS_CONFIG_DESC L"description"
22#define IIS_CONFIG_EXECUTABLE L"scriptProcessor"
23#define IIS_CONFIG_ENABLED L"enabled"
24#define IIS_CONFIG_ENABLE32 L"enable32BitAppOnWin64"
25#define IIS_CONFIG_FILEEXT L"fileExtension"
26#define IIS_CONFIG_FILTER L"filter"
27#define IIS_CONFIG_GROUPID L"groupId"
28#define IIS_CONFIG_HEADERS L"customHeaders"
29#define IIS_CONFIG_HTTPERRORS_SECTION L"system.webServer/httpErrors"
30#define IIS_CONFIG_ID L"id"
31#define IIS_CONFIG_ISAPI_SECTION L"system.webServer/isapiFilters"
32#define IIS_CONFIG_HTTPPROTO_SECTION L"system.webServer/httpProtocol"
33#define IIS_CONFIG_LOG_SECTION L"system.applicationHost/log"
34#define IIS_CONFIG_LOG_UTF8 L"logInUTF8"
35#define IIS_CONFIG_LIMITS L"limits"
36#define IIS_CONFIG_PIPELINEMODE L"managedPipelineMode"
37#define IIS_CONFIG_MANAGEDRUNTIMEVERSION L"managedRuntimeVersion"
38#define IIS_CONFIG_WEBLOG L"logFile"
39#define IIS_CONFIG_LOGFORMAT L"logFormat"
40#define IIS_CONFIG_MIMEMAP L"mimeMap"
41#define IIS_CONFIG_MIMETYPE L"mimeType"
42#define IIS_CONFIG_MODULES L"modules"
43#define IIS_CONFIG_NAME L"name"
44#define IIS_CONFIG_PATH L"path"
45#define IIS_CONFIG_PHYSPATH L"physicalPath"
46#define IIS_CONFIG_PROTOCOL L"protocol"
47#define IIS_CONFIG_RESTRICTION_SECTION L"system.webServer/security/isapiCgiRestriction"
48#define IIS_CONFIG_SITE L"site"
49#define IIS_CONFIG_SITE_ID L"id"
50#define IIS_CONFIG_SITES_SECTION L"system.applicationHost/sites"
51#define IIS_CONFIG_CONNECTTIMEOUT L"connectionTimeout"
52#define IIS_CONFIG_VDIR L"virtualDirectory"
53#define IIS_CONFIG_VALUE L"value"
54#define IIS_CONFIG_VERBS L"verb"
55#define IIS_CONFIG_WEBLIMITS_SECTION L"system.applicationHost/webLimits"
56#define IIS_CONFIG_WEBLIMITS_MAXBAND L"maxGlobalBandwidth"
57#define IIS_CONFIG_TRUE L"true"
58#define IIS_CONFIG_FALSE L"false"
59#define IIS_CONFIG_ERROR L"error"
60#define IIS_CONFIG_STATUSCODE L"statusCode"
61#define IIS_CONFIG_SUBSTATUS L"subStatusCode"
62#define IIS_CONFIG_LANGPATH L"prefixLanguageFilePath"
63#define IIS_CONFIG_RESPMODE L"responseMode"
64#define IIS_CONFIG_CLEAR L"clear"
65#define IIS_CONFIG_RECYCLING L"recycling"
66#define IIS_CONFIG_PEROIDRESTART L"periodicRestart"
67#define IIS_CONFIG_TIME L"time"
68#define IIS_CONFIG_REQUESTS L"requests"
69#define IIS_CONFIG_SCHEDULE L"schedule"
70#define IIS_CONFIG_MEMORY L"memory"
71#define IIS_CONFIG_PRIVMEMORY L"privateMemory"
72#define IIS_CONFIG_PROCESSMODEL L"processModel"
73#define IIS_CONFIG_IDLETIMEOUT L"idleTimeout"
74#define IIS_CONFIG_QUEUELENGTH L"queueLength"
75#define IIS_CONFIG_IDENITITYTYPE L"identityType"
76#define IIS_CONFIG_LOCALSYSTEM L"LocalSystem"
77#define IIS_CONFIG_LOCALSERVICE L"LocalService"
78#define IIS_CONFIG_NETWORKSERVICE L"NetworkService"
79#define IIS_CONFIG_SPECIFICUSER L"SpecificUser"
80#define IIS_CONFIG_APPLICATIONPOOLIDENTITY L"ApplicationPoolIdentity"
81#define IIS_CONFIG_USERNAME L"userName"
82#define IIS_CONFIG_PASSWORD L"password"
83#define IIS_CONFIG_CPU L"cpu"
84#define IIS_CONFIG_LIMIT L"limit"
85#define IIS_CONFIG_CPU_ACTION L"action"
86#define IIS_CONFIG_KILLW3WP L"KillW3wp"
87#define IIS_CONFIG_NOACTION L"NoAction"
88#define IIS_CONFIG_RESETINTERVAL L"resetInterval"
89#define IIS_CONFIG_MAXWRKPROCESSES L"maxProcesses"
90#define IIS_CONFIG_HANDLERS_SECTION L"system.webServer/handlers"
91#define IIS_CONFIG_DEFAULTDOC_SECTION L"system.webServer/defaultDocument"
92#define IIS_CONFIG_ASP_SECTION L"system.webServer/asp"
93#define IIS_CONFIG_SCRIPTERROR L"scriptErrorSentToBrowser"
94#define IIS_CONFIG_STATICCONTENT_SECTION L"system.webServer/staticContent"
95#define IIS_CONFIG_HTTPEXPIRES L"httpExpires"
96#define IIS_CONFIG_MAXAGE L"cacheControlMaxAge"
97#define IIS_CONFIG_CLIENTCACHE L"clientCache"
98#define IIS_CONFIG_CACHECONTROLMODE L"cacheControlMode"
99#define IIS_CONFIG_USEMAXAGE L"UseMaxAge"
100#define IIS_CONFIG_USEEXPIRES L"UseExpires"
101#define IIS_CONFIG_CACHECUST L"cacheControlCustom"
102#define IIS_CONFIG_ASP_SECTION L"system.webServer/asp"
103#define IIS_CONFIG_SESSION L"session"
104#define IIS_CONFIG_ALLOWSTATE L"allowSessionState"
105#define IIS_CONFIG_TIMEOUT L"timeout"
106#define IIS_CONFIG_BUFFERING L"bufferingOn"
107#define IIS_CONFIG_PARENTPATHS L"enableParentPaths"
108#define IIS_CONFIG_SCRIPTLANG L"scriptLanguage"
109#define IIS_CONFIG_SCRIPTTIMEOUT L"scriptTimeout"
110#define IIS_CONFIG_LIMITS L"limits"
111#define IIS_CONFIG_ALLOWDEBUG L"appAllowDebugging"
112#define IIS_CONFIG_ALLOWCLIENTDEBUG L"appAllowClientDebug"
113#define IIS_CONFIG_CERTIFICATEHASH L"certificateHash"
114#define IIS_CONFIG_CERTIFICATESTORENAME L"certificateStoreName"
115#define IIS_CONFIG_HTTPLOGGING_SECTION L"system.webServer/httpLogging"
116#define IIS_CONFIG_DONTLOG L"dontLog"
117
118typedef BOOL (CALLBACK* ENUMAPHOSTELEMENTPROC)(IAppHostElement*, LPVOID);
119typedef BOOL (CALLBACK* VARIANTCOMPARATORPROC)(VARIANT*, VARIANT*);
120
121HRESULT DAPI Iis7PutPropertyVariant(
122 __in IAppHostElement *pElement,
123 __in LPCWSTR wzPropName,
124 __in VARIANT vtPut
125 );
126
127HRESULT DAPI Iis7PutPropertyInteger(
128 __in IAppHostElement *pElement,
129 __in LPCWSTR wzPropName,
130 __in DWORD dValue
131 );
132
133HRESULT DAPI Iis7PutPropertyString(
134 __in IAppHostElement *pElement,
135 __in LPCWSTR wzPropName,
136 __in LPCWSTR wzString
137 );
138
139HRESULT DAPI Iis7PutPropertyBool(
140 __in IAppHostElement *pElement,
141 __in LPCWSTR wzPropName,
142 __in BOOL fValue);
143
144HRESULT DAPI Iis7GetPropertyVariant(
145 __in IAppHostElement *pElement,
146 __in LPCWSTR wzPropName,
147 __in VARIANT* vtGet
148 );
149
150HRESULT DAPI Iis7GetPropertyString(
151 __in IAppHostElement *pElement,
152 __in LPCWSTR wzPropName,
153 __in LPWSTR* psczGet
154 );
155
156struct IIS7_APPHOSTELEMENTCOMPARISON
157{
158 LPCWSTR sczElementName;
159 LPCWSTR sczAttributeName;
160 VARIANT* pvAttributeValue;
161 VARIANTCOMPARATORPROC pComparator;
162};
163
164BOOL DAPI Iis7IsMatchingAppHostElement(
165 __in IAppHostElement *pElement,
166 __in IIS7_APPHOSTELEMENTCOMPARISON* pComparison
167 );
168
169HRESULT DAPI Iis7FindAppHostElementString(
170 __in IAppHostElementCollection *pCollection,
171 __in LPCWSTR wzElementName,
172 __in LPCWSTR wzAttributeName,
173 __in LPCWSTR wzAttributeValue,
174 __out IAppHostElement** ppElement,
175 __out DWORD* pdwIndex
176 );
177
178HRESULT DAPI Iis7FindAppHostElementPath(
179 __in IAppHostElementCollection *pCollection,
180 __in LPCWSTR wzElementName,
181 __in LPCWSTR wzAttributeName,
182 __in LPCWSTR wzAttributeValue,
183 __out IAppHostElement** ppElement,
184 __out DWORD* pdwIndex
185 );
186
187HRESULT DAPI Iis7FindAppHostElementInteger(
188 __in IAppHostElementCollection *pCollection,
189 __in LPCWSTR wzElementName,
190 __in LPCWSTR wzAttributeName,
191 __in DWORD dwAttributeValue,
192 __out IAppHostElement** ppElement,
193 __out DWORD* pdwIndex
194 );
195
196HRESULT DAPI Iis7FindAppHostElementVariant(
197 __in IAppHostElementCollection *pCollection,
198 __in LPCWSTR wzElementName,
199 __in LPCWSTR wzAttributeName,
200 __in VARIANT* pvAttributeValue,
201 __out IAppHostElement** ppElement,
202 __out DWORD* pdwIndex
203 );
204
205HRESULT DAPI Iis7EnumAppHostElements(
206 __in IAppHostElementCollection *pCollection,
207 __in ENUMAPHOSTELEMENTPROC pCallback,
208 __in LPVOID pContext,
209 __out IAppHostElement** ppElement,
210 __out DWORD* pdwIndex
211 );
212
213HRESULT DAPI Iis7FindAppHostMethod(
214 __in IAppHostMethodCollection *pCollection,
215 __in LPCWSTR wzMethodName,
216 __out IAppHostMethod** ppMethod,
217 __out DWORD* pdwIndex
218 );
219
220#ifdef __cplusplus
221}
222#endif
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/inetutil.h b/src/libs/dutil/WixToolset.DUtil/inc/inetutil.h
new file mode 100644
index 00000000..19ace88b
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/inetutil.h
@@ -0,0 +1,39 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9#define ReleaseInternet(h) if (h) { ::InternetCloseHandle(h); h = NULL; }
10#define ReleaseNullInternet(h) if (h) { ::InternetCloseHandle(h); h = NULL; }
11
12
13// functions
14HRESULT DAPI InternetGetSizeByHandle(
15 __in HINTERNET hiFile,
16 __out LONGLONG* pllSize
17 );
18
19HRESULT DAPI InternetGetCreateTimeByHandle(
20 __in HINTERNET hiFile,
21 __out LPFILETIME pft
22 );
23
24HRESULT DAPI InternetQueryInfoString(
25 __in HINTERNET h,
26 __in DWORD dwInfo,
27 __deref_out_z LPWSTR* psczValue
28 );
29
30HRESULT DAPI InternetQueryInfoNumber(
31 __in HINTERNET h,
32 __in DWORD dwInfo,
33 __inout LONG* plInfo
34 );
35
36#ifdef __cplusplus
37}
38#endif
39
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/iniutil.h b/src/libs/dutil/WixToolset.DUtil/inc/iniutil.h
new file mode 100644
index 00000000..c8503155
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/iniutil.h
@@ -0,0 +1,79 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9#define ReleaseIni(ih) if (ih) { IniUninitialize(ih); }
10#define ReleaseNullIni(ih) if (ih) { IniUninitialize(ih); ih = NULL; }
11
12typedef void* INI_HANDLE;
13typedef const void* C_INI_HANDLE;
14
15extern const int INI_HANDLE_BYTES;
16
17struct INI_VALUE
18{
19 LPCWSTR wzName;
20 LPCWSTR wzValue;
21
22 DWORD dwLineNumber;
23};
24
25HRESULT DAPI IniInitialize(
26 __out_bcount(INI_HANDLE_BYTES) INI_HANDLE* piHandle
27 );
28void DAPI IniUninitialize(
29 __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle
30 );
31HRESULT DAPI IniSetOpenTag(
32 __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle,
33 __in_z_opt LPCWSTR wzOpenTagPrefix,
34 __in_z_opt LPCWSTR wzOpenTagPostfix
35 );
36HRESULT DAPI IniSetValueStyle(
37 __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle,
38 __in_z_opt LPCWSTR wzValuePrefix,
39 __in_z_opt LPCWSTR wzValueSeparator
40 );
41HRESULT DAPI IniSetValueSeparatorException(
42 __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle,
43 __in_z LPCWSTR wzValueNamePrefix
44 );
45HRESULT DAPI IniSetCommentStyle(
46 __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle,
47 __in_z_opt LPCWSTR wzLinePrefix
48 );
49HRESULT DAPI IniParse(
50 __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle,
51 __in LPCWSTR wzPath,
52 __out_opt FILE_ENCODING *pfeEncodingFound
53 );
54// Gets the full value array, this includes values that may have been deleted
55// (their value will be NULL)
56HRESULT DAPI IniGetValueList(
57 __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle,
58 __deref_out_ecount_opt(*pcValues) INI_VALUE** prgivValues,
59 __out DWORD *pcValues
60 );
61HRESULT DAPI IniGetValue(
62 __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle,
63 __in LPCWSTR wzValueName,
64 __deref_out_z LPWSTR* psczValue
65 );
66HRESULT DAPI IniSetValue(
67 __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle,
68 __in LPCWSTR wzValueName,
69 __in_z_opt LPCWSTR wzValue
70 );
71HRESULT DAPI IniWriteFile(
72 __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle,
73 __in_z_opt LPCWSTR wzPath,
74 __in FILE_ENCODING feOverrideEncoding
75 );
76
77#ifdef __cplusplus
78}
79#endif
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/jsonutil.h b/src/libs/dutil/WixToolset.DUtil/inc/jsonutil.h
new file mode 100644
index 00000000..b05e9970
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/jsonutil.h
@@ -0,0 +1,112 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9typedef enum JSON_TOKEN
10{
11 JSON_TOKEN_NONE,
12 JSON_TOKEN_ARRAY_START,
13 JSON_TOKEN_ARRAY_VALUE,
14 JSON_TOKEN_ARRAY_END,
15 JSON_TOKEN_OBJECT_START,
16 JSON_TOKEN_OBJECT_KEY,
17 JSON_TOKEN_OBJECT_VALUE,
18 JSON_TOKEN_OBJECT_END,
19 JSON_TOKEN_VALUE,
20} JSON_TOKEN;
21
22typedef struct _JSON_VALUE
23{
24} JSON_VALUE;
25
26typedef struct _JSON_READER
27{
28 CRITICAL_SECTION cs;
29 LPWSTR sczJson;
30
31 LPWSTR pwz;
32 JSON_TOKEN token;
33} JSON_READER;
34
35typedef struct _JSON_WRITER
36{
37 CRITICAL_SECTION cs;
38 LPWSTR sczJson;
39
40 JSON_TOKEN* rgTokenStack;
41 DWORD cTokens;
42 DWORD cMaxTokens;
43} JSON_WRITER;
44
45
46DAPI_(HRESULT) JsonInitializeReader(
47 __in_z LPCWSTR wzJson,
48 __in JSON_READER* pReader
49 );
50
51DAPI_(void) JsonUninitializeReader(
52 __in JSON_READER* pReader
53 );
54
55DAPI_(HRESULT) JsonReadNext(
56 __in JSON_READER* pReader,
57 __out JSON_TOKEN* pToken,
58 __out JSON_VALUE* pValue
59 );
60
61DAPI_(HRESULT) JsonReadValue(
62 __in JSON_READER* pReader,
63 __in JSON_VALUE* pValue
64 );
65
66DAPI_(HRESULT) JsonInitializeWriter(
67 __in JSON_WRITER* pWriter
68 );
69
70DAPI_(void) JsonUninitializeWriter(
71 __in JSON_WRITER* pWriter
72 );
73
74DAPI_(HRESULT) JsonWriteBool(
75 __in JSON_WRITER* pWriter,
76 __in BOOL fValue
77 );
78
79DAPI_(HRESULT) JsonWriteNumber(
80 __in JSON_WRITER* pWriter,
81 __in DWORD dwValue
82 );
83
84DAPI_(HRESULT) JsonWriteString(
85 __in JSON_WRITER* pWriter,
86 __in_z LPCWSTR wzValue
87 );
88
89DAPI_(HRESULT) JsonWriteArrayStart(
90 __in JSON_WRITER* pWriter
91 );
92
93DAPI_(HRESULT) JsonWriteArrayEnd(
94 __in JSON_WRITER* pWriter
95 );
96
97DAPI_(HRESULT) JsonWriteObjectStart(
98 __in JSON_WRITER* pWriter
99 );
100
101DAPI_(HRESULT) JsonWriteObjectKey(
102 __in JSON_WRITER* pWriter,
103 __in_z LPCWSTR wzKey
104 );
105
106DAPI_(HRESULT) JsonWriteObjectEnd(
107 __in JSON_WRITER* pWriter
108 );
109
110#ifdef __cplusplus
111}
112#endif
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/locutil.h b/src/libs/dutil/WixToolset.DUtil/inc/locutil.h
new file mode 100644
index 00000000..38ddda20
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/locutil.h
@@ -0,0 +1,120 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9struct LOC_STRING
10{
11 LPWSTR wzId;
12 LPWSTR wzText;
13 BOOL bOverridable;
14};
15
16const int LOC_CONTROL_NOT_SET = INT_MAX;
17
18struct LOC_CONTROL
19{
20 LPWSTR wzControl;
21 int nX;
22 int nY;
23 int nWidth;
24 int nHeight;
25 LPWSTR wzText;
26};
27
28const int WIX_LOCALIZATION_LANGUAGE_NOT_SET = INT_MAX;
29
30struct WIX_LOCALIZATION
31{
32 DWORD dwLangId;
33
34 DWORD cLocStrings;
35 LOC_STRING* rgLocStrings;
36
37 DWORD cLocControls;
38 LOC_CONTROL* rgLocControls;
39};
40
41/********************************************************************
42 LocProbeForFile - Searches for a localization file on disk.
43
44*******************************************************************/
45HRESULT DAPI LocProbeForFile(
46 __in_z LPCWSTR wzBasePath,
47 __in_z LPCWSTR wzLocFileName,
48 __in_z_opt LPCWSTR wzLanguage,
49 __inout LPWSTR* psczPath
50 );
51
52/********************************************************************
53 LocLoadFromFile - Loads a localization file
54
55*******************************************************************/
56HRESULT DAPI LocLoadFromFile(
57 __in_z LPCWSTR wzWxlFile,
58 __out WIX_LOCALIZATION** ppWixLoc
59 );
60
61/********************************************************************
62 LocLoadFromResource - loads a localization file from a module's data
63 resource.
64
65 NOTE: The resource data must be UTF-8 encoded.
66*******************************************************************/
67HRESULT DAPI LocLoadFromResource(
68 __in HMODULE hModule,
69 __in_z LPCSTR szResource,
70 __out WIX_LOCALIZATION** ppWixLoc
71 );
72
73/********************************************************************
74 LocFree - free memory allocated when loading a localization file
75
76*******************************************************************/
77void DAPI LocFree(
78 __in_opt WIX_LOCALIZATION* pWixLoc
79 );
80
81/********************************************************************
82 LocLocalizeString - replace any #(loc.id) in a string with the
83 correct sub string
84*******************************************************************/
85HRESULT DAPI LocLocalizeString(
86 __in const WIX_LOCALIZATION* pWixLoc,
87 __inout LPWSTR* psczInput
88 );
89
90/********************************************************************
91 LocGetControl - returns a control's localization information
92*******************************************************************/
93HRESULT DAPI LocGetControl(
94 __in const WIX_LOCALIZATION* pWixLoc,
95 __in_z LPCWSTR wzId,
96 __out LOC_CONTROL** ppLocControl
97 );
98
99/********************************************************************
100LocGetString - returns a string's localization information
101*******************************************************************/
102extern "C" HRESULT DAPI LocGetString(
103 __in const WIX_LOCALIZATION* pWixLoc,
104 __in_z LPCWSTR wzId,
105 __out LOC_STRING** ppLocString
106 );
107
108/********************************************************************
109LocAddString - adds a localization string
110*******************************************************************/
111extern "C" HRESULT DAPI LocAddString(
112 __in WIX_LOCALIZATION* pWixLoc,
113 __in_z LPCWSTR wzId,
114 __in_z LPCWSTR wzLocString,
115 __in BOOL bOverridable
116 );
117
118#ifdef __cplusplus
119}
120#endif
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/logutil.h b/src/libs/dutil/WixToolset.DUtil/inc/logutil.h
new file mode 100644
index 00000000..426506ee
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/logutil.h
@@ -0,0 +1,192 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9#define LogExitOnFailureSource(d, x, i, f, ...) if (FAILED(x)) { LogErrorId(x, i, __VA_ARGS__); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; }
10#define LogExitOnRootFailureSource(d, x, i, f, ...) if (FAILED(x)) { LogErrorId(x, i, __VA_ARGS__); Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceSource(d, x, f, __VA_ARGS__); goto LExit; }
11
12#define LogExitOnFailure(x, i, f, ...) LogExitOnFailureSource(DUTIL_SOURCE_DEFAULT, x, i, f, __VA_ARGS__)
13#define LogExitOnRootFailure(x, i, f, ...) LogExitOnRootFailureSource(DUTIL_SOURCE_DEFAULT, x, i, f, __VA_ARGS__)
14
15typedef HRESULT (DAPI *PFN_LOGSTRINGWORKRAW)(
16 __in_z LPCSTR szString,
17 __in_opt LPVOID pvContext
18 );
19
20// enums
21
22// structs
23
24// functions
25BOOL DAPI IsLogInitialized();
26
27BOOL DAPI IsLogOpen();
28
29void DAPI LogInitialize(
30 __in HMODULE hModule
31 );
32
33HRESULT DAPI LogOpen(
34 __in_z_opt LPCWSTR wzDirectory,
35 __in_z LPCWSTR wzLog,
36 __in_z_opt LPCWSTR wzPostfix,
37 __in_z_opt LPCWSTR wzExt,
38 __in BOOL fAppend,
39 __in BOOL fHeader,
40 __out_z_opt LPWSTR* psczLogPath
41 );
42
43void DAPI LogDisable();
44
45void DAPI LogRedirect(
46 __in_opt PFN_LOGSTRINGWORKRAW vpfLogStringWorkRaw,
47 __in_opt LPVOID pvContext
48 );
49
50HRESULT DAPI LogRename(
51 __in_z LPCWSTR wzNewPath
52 );
53
54void DAPI LogClose(
55 __in BOOL fFooter
56 );
57
58void DAPI LogUninitialize(
59 __in BOOL fFooter
60 );
61
62BOOL DAPI LogIsOpen();
63
64HRESULT DAPI LogSetSpecialParams(
65 __in_z_opt LPCWSTR wzSpecialBeginLine,
66 __in_z_opt LPCWSTR wzSpecialAfterTimeStamp,
67 __in_z_opt LPCWSTR wzSpecialEndLine
68 );
69
70REPORT_LEVEL DAPI LogSetLevel(
71 __in REPORT_LEVEL rl,
72 __in BOOL fLogChange
73 );
74
75REPORT_LEVEL DAPI LogGetLevel();
76
77HRESULT DAPI LogGetPath(
78 __out_ecount_z(cchLogPath) LPWSTR pwzLogPath,
79 __in DWORD cchLogPath
80 );
81
82HANDLE DAPI LogGetHandle();
83
84HRESULT DAPIV LogString(
85 __in REPORT_LEVEL rl,
86 __in_z __format_string LPCSTR szFormat,
87 ...
88 );
89
90HRESULT DAPI LogStringArgs(
91 __in REPORT_LEVEL rl,
92 __in_z __format_string LPCSTR szFormat,
93 __in va_list args
94 );
95
96HRESULT DAPIV LogStringLine(
97 __in REPORT_LEVEL rl,
98 __in_z __format_string LPCSTR szFormat,
99 ...
100 );
101
102HRESULT DAPI LogStringLineArgs(
103 __in REPORT_LEVEL rl,
104 __in_z __format_string LPCSTR szFormat,
105 __in va_list args
106 );
107
108HRESULT DAPI LogIdModuleArgs(
109 __in REPORT_LEVEL rl,
110 __in DWORD dwLogId,
111 __in_opt HMODULE hModule,
112 __in va_list args
113 );
114
115/*
116 * Wraps LogIdModuleArgs, so inline to save the function call
117 */
118
119inline HRESULT LogId(
120 __in REPORT_LEVEL rl,
121 __in DWORD dwLogId,
122 ...
123 )
124{
125 HRESULT hr = S_OK;
126 va_list args;
127
128 va_start(args, dwLogId);
129 hr = LogIdModuleArgs(rl, dwLogId, NULL, args);
130 va_end(args);
131
132 return hr;
133}
134
135
136/*
137 * Wraps LogIdModuleArgs, so inline to save the function call
138 */
139
140inline HRESULT LogIdArgs(
141 __in REPORT_LEVEL rl,
142 __in DWORD dwLogId,
143 __in va_list args
144 )
145{
146 return LogIdModuleArgs(rl, dwLogId, NULL, args);
147}
148
149HRESULT DAPIV LogErrorString(
150 __in HRESULT hrError,
151 __in_z __format_string LPCSTR szFormat,
152 ...
153 );
154
155HRESULT DAPI LogErrorStringArgs(
156 __in HRESULT hrError,
157 __in_z __format_string LPCSTR szFormat,
158 __in va_list args
159 );
160
161HRESULT DAPI LogErrorIdModule(
162 __in HRESULT hrError,
163 __in DWORD dwLogId,
164 __in_opt HMODULE hModule,
165 __in_z_opt LPCWSTR wzString1,
166 __in_z_opt LPCWSTR wzString2,
167 __in_z_opt LPCWSTR wzString3
168 );
169
170inline HRESULT LogErrorId(
171 __in HRESULT hrError,
172 __in DWORD dwLogId,
173 __in_z_opt LPCWSTR wzString1 = NULL,
174 __in_z_opt LPCWSTR wzString2 = NULL,
175 __in_z_opt LPCWSTR wzString3 = NULL
176 )
177{
178 return LogErrorIdModule(hrError, dwLogId, NULL, wzString1, wzString2, wzString3);
179}
180
181HRESULT DAPI LogHeader();
182
183HRESULT DAPI LogFooter();
184
185HRESULT LogStringWorkRaw(
186 __in_z LPCSTR szLogData
187 );
188
189#ifdef __cplusplus
190}
191#endif
192
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/memutil.h b/src/libs/dutil/WixToolset.DUtil/inc/memutil.h
new file mode 100644
index 00000000..49f86e0a
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/memutil.h
@@ -0,0 +1,80 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9#define ReleaseMem(p) if (p) { MemFree(p); }
10#define ReleaseNullMem(p) if (p) { MemFree(p); p = NULL; }
11
12HRESULT DAPI MemInitialize();
13void DAPI MemUninitialize();
14
15LPVOID DAPI MemAlloc(
16 __in SIZE_T cbSize,
17 __in BOOL fZero
18 );
19LPVOID DAPI MemReAlloc(
20 __in LPVOID pv,
21 __in SIZE_T cbSize,
22 __in BOOL fZero
23 );
24HRESULT DAPI MemReAllocSecure(
25 __in LPVOID pv,
26 __in SIZE_T cbSize,
27 __in BOOL fZero,
28 __deref_out LPVOID* ppvNew
29 );
30HRESULT DAPI MemAllocArray(
31 __inout LPVOID* ppvArray,
32 __in SIZE_T cbArrayType,
33 __in DWORD dwItemCount
34 );
35HRESULT DAPI MemReAllocArray(
36 __inout LPVOID* ppvArray,
37 __in DWORD cArray,
38 __in SIZE_T cbArrayType,
39 __in DWORD dwNewItemCount
40 );
41HRESULT DAPI MemEnsureArraySize(
42 __deref_inout_bcount(cArray * cbArrayType) LPVOID* ppvArray,
43 __in DWORD cArray,
44 __in SIZE_T cbArrayType,
45 __in DWORD dwGrowthCount
46 );
47HRESULT DAPI MemInsertIntoArray(
48 __deref_inout_bcount((cExistingArray + cInsertItems) * cbArrayType) LPVOID* ppvArray,
49 __in DWORD dwInsertIndex,
50 __in DWORD cInsertItems,
51 __in DWORD cExistingArray,
52 __in SIZE_T cbArrayType,
53 __in DWORD dwGrowthCount
54 );
55void DAPI MemRemoveFromArray(
56 __inout_bcount((cExistingArray) * cbArrayType) LPVOID pvArray,
57 __in DWORD dwRemoveIndex,
58 __in DWORD cRemoveItems,
59 __in DWORD cExistingArray,
60 __in SIZE_T cbArrayType,
61 __in BOOL fPreserveOrder
62 );
63void DAPI MemArraySwapItems(
64 __inout_bcount(cbArrayType) LPVOID pvArray,
65 __in DWORD dwIndex1,
66 __in DWORD dwIndex2,
67 __in SIZE_T cbArrayType
68 );
69
70HRESULT DAPI MemFree(
71 __in LPVOID pv
72 );
73SIZE_T DAPI MemSize(
74 __in LPCVOID pv
75 );
76
77#ifdef __cplusplus
78}
79#endif
80
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/metautil.h b/src/libs/dutil/WixToolset.DUtil/inc/metautil.h
new file mode 100644
index 00000000..31f9ff5c
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/metautil.h
@@ -0,0 +1,52 @@
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 <iadmw.h>
6#include <iiscnfg.h>
7#include <iwamreg.h>
8#include <mddefw.h>
9
10#ifdef __cplusplus
11extern "C" {
12#endif
13
14// structs
15
16// prototypes
17HRESULT DAPI MetaFindWebBase(
18 __in IMSAdminBaseW* piMetabase,
19 __in_z LPCWSTR wzIP,
20 __in int iPort,
21 __in_z LPCWSTR wzHeader,
22 __in BOOL fSecure,
23 __out_ecount(cchWebBase) LPWSTR wzWebBase,
24 __in DWORD cchWebBase
25 );
26HRESULT DAPI MetaFindFreeWebBase(
27 __in IMSAdminBaseW* piMetabase,
28 __out_ecount(cchWebBase) LPWSTR wzWebBase,
29 __in DWORD cchWebBase
30 );
31
32HRESULT DAPI MetaOpenKey(
33 __in IMSAdminBaseW* piMetabase,
34 __in METADATA_HANDLE mhKey,
35 __in_z LPCWSTR wzKey,
36 __in DWORD dwAccess,
37 __in DWORD cRetries,
38 __out METADATA_HANDLE* pmh
39 );
40HRESULT DAPI MetaGetValue(
41 __in IMSAdminBaseW* piMetabase,
42 __in METADATA_HANDLE mhKey,
43 __in_z LPCWSTR wzKey,
44 __inout METADATA_RECORD* pmr
45 );
46void DAPI MetaFreeValue(
47 __in METADATA_RECORD* pmr
48 );
49
50#ifdef __cplusplus
51}
52#endif
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/monutil.h b/src/libs/dutil/WixToolset.DUtil/inc/monutil.h
new file mode 100644
index 00000000..f644e205
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/monutil.h
@@ -0,0 +1,108 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9#define ReleaseMon(mh) if (mh) { MonDestroy(mh); }
10#define ReleaseNullMon(mh) if (mh) { MonDestroy(mh); mh = NULL; }
11
12typedef void* MON_HANDLE;
13typedef const void* C_MON_HANDLE;
14
15// Defined in regutil.h
16enum REG_KEY_BITNESS;
17
18extern const int MON_HANDLE_BYTES;
19
20// Note: callbacks must be implemented in a thread-safe manner. They will be called asynchronously by a MonUtil-spawned thread.
21// They must also be written to return as soon as possible - they are called from the waiter thread
22typedef void (*PFN_MONGENERAL)(
23 __in HRESULT hr,
24 __in_opt LPVOID pvContext
25 );
26// This callback is not specific to any wait - it will notify client of any drive status changes, such as removable drive insertion / removal
27typedef void (*PFN_MONDRIVESTATUS)(
28 __in WCHAR chDrive,
29 __in BOOL fArriving,
30 __in_opt LPVOID pvContext
31 );
32// Note if these fire with a failed result it means an error has occurred with the wait, and so the wait will stay in the list and be retried. When waits start succeeding again,
33// MonUtil will notify of changes, because it may have not noticed changes during the interval for which the wait had failed. This behavior can result in false positive notifications,
34// so all consumers of MonUtil should be designed with this in mind.
35typedef void (*PFN_MONDIRECTORY)(
36 __in HRESULT hr,
37 __in_z LPCWSTR wzPath,
38 __in BOOL fRecursive,
39 __in_opt LPVOID pvContext,
40 __in_opt LPVOID pvDirectoryContext
41 );
42typedef void (*PFN_MONREGKEY)(
43 __in HRESULT hr,
44 __in HKEY hkRoot,
45 __in_z LPCWSTR wzSubKey,
46 __in REG_KEY_BITNESS kbKeyBitness,
47 __in BOOL fRecursive,
48 __in_opt LPVOID pvContext,
49 __in_opt LPVOID pvRegKeyContext
50 );
51
52// Silence period allows you to avoid lots of notifications when a lot of writes are going on in a directory
53// MonUtil will wait until the directory has been "silent" for at least dwSilencePeriodInMs milliseconds
54// The drawback to setting this to a value higher than zero is that even single write notifications
55// are delayed by this amount
56HRESULT DAPI MonCreate(
57 __out_bcount(MON_HANDLE_BYTES) MON_HANDLE *pHandle,
58 __in PFN_MONGENERAL vpfMonGeneral,
59 __in_opt PFN_MONDRIVESTATUS vpfMonDriveStatus,
60 __in_opt PFN_MONDIRECTORY vpfMonDirectory,
61 __in_opt PFN_MONREGKEY vpfMonRegKey,
62 __in_opt LPVOID pvContext
63 );
64// Don't add multiple identical waits! Not only is it wasteful and will cause multiple fires for the exact same change, it will also
65// result in slightly odd behavior when you remove a duplicated wait (removing a wait may or may not remove multiple waits)
66// This is due to the way coordinator thread and waiter threads handle removing, and while it is possible to solve, doing so would complicate the code.
67// So instead, de-dupe your wait requests before sending them to MonUtil.
68// Special notes for network waits: MonUtil can send false positive notifications (i.e. notifications when nothing had changed) if connection
69// to the share is lost and reconnected, because MonUtil can't know for sure whether changes occurred while the connection was lost.
70// Also, MonUtil will very every 20 minutes retry even successful network waits, because the underlying Win32 API cannot notify us if a remote server
71// had its network cable unplugged or similar sudden failure. When we retry the successful network waits, we will also send a false positive notification,
72// because it's impossible for MonUtil to detect if we're reconnecting to a server that had died and come back to life, or if we're reconnecting to a server that had
73// been up all along. For both of the above reasons, clients of MonUtil must be written to do very, very little work in the case of false positive network waits.
74HRESULT DAPI MonAddDirectory(
75 __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle,
76 __in_z LPCWSTR wzPath,
77 __in BOOL fRecursive,
78 __in DWORD dwSilencePeriodInMs,
79 __in_opt LPVOID pvDirectoryContext
80 );
81HRESULT DAPI MonAddRegKey(
82 __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle,
83 __in HKEY hkRoot,
84 __in_z LPCWSTR wzSubKey,
85 __in REG_KEY_BITNESS kbKeyBitness,
86 __in BOOL fRecursive,
87 __in DWORD dwSilencePeriodInMs,
88 __in_opt LPVOID pvRegKeyContext
89 );
90HRESULT DAPI MonRemoveDirectory(
91 __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle,
92 __in_z LPCWSTR wzPath,
93 __in BOOL fRecursive
94 );
95HRESULT DAPI MonRemoveRegKey(
96 __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle,
97 __in HKEY hkRoot,
98 __in_z LPCWSTR wzSubKey,
99 __in REG_KEY_BITNESS kbKeyBitness,
100 __in BOOL fRecursive
101 );
102void DAPI MonDestroy(
103 __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle
104 );
105
106#ifdef __cplusplus
107}
108#endif
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/osutil.h b/src/libs/dutil/WixToolset.DUtil/inc/osutil.h
new file mode 100644
index 00000000..2cce6f63
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/osutil.h
@@ -0,0 +1,42 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9typedef enum OS_VERSION
10{
11 OS_VERSION_UNKNOWN,
12 OS_VERSION_WINNT,
13 OS_VERSION_WIN2000,
14 OS_VERSION_WINXP,
15 OS_VERSION_WIN2003,
16 OS_VERSION_VISTA,
17 OS_VERSION_WIN2008,
18 OS_VERSION_WIN7,
19 OS_VERSION_WIN2008_R2,
20 OS_VERSION_FUTURE
21} OS_VERSION;
22
23void DAPI OsGetVersion(
24 __out OS_VERSION* pVersion,
25 __out DWORD* pdwServicePack
26 );
27HRESULT DAPI OsCouldRunPrivileged(
28 __out BOOL* pfPrivileged
29 );
30HRESULT DAPI OsIsRunningPrivileged(
31 __out BOOL* pfPrivileged
32 );
33HRESULT DAPI OsIsUacEnabled(
34 __out BOOL* pfUacEnabled
35 );
36HRESULT DAPI OsRtlGetVersion(
37 __inout RTL_OSVERSIONINFOEXW* pOvix
38 );
39
40#ifdef __cplusplus
41}
42#endif
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/pathutil.h b/src/libs/dutil/WixToolset.DUtil/inc/pathutil.h
new file mode 100644
index 00000000..579b8454
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/pathutil.h
@@ -0,0 +1,252 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9typedef enum PATH_EXPAND
10{
11 PATH_EXPAND_ENVIRONMENT = 0x0001,
12 PATH_EXPAND_FULLPATH = 0x0002,
13} PATH_EXPAND;
14
15
16/*******************************************************************
17 PathCommandLineAppend - appends a command line argument on to a
18 string such that ::CommandLineToArgv() will shred them correctly
19 (i.e. quote arguments with spaces in them).
20********************************************************************/
21DAPI_(HRESULT) PathCommandLineAppend(
22 __deref_inout_z LPWSTR* psczCommandLine,
23 __in_z LPCWSTR wzArgument
24 );
25
26/*******************************************************************
27 PathFile - returns a pointer to the file part of the path.
28********************************************************************/
29DAPI_(LPWSTR) PathFile(
30 __in_z LPCWSTR wzPath
31 );
32
33/*******************************************************************
34 PathExtension - returns a pointer to the extension part of the path
35 (including the dot).
36********************************************************************/
37DAPI_(LPCWSTR) PathExtension(
38 __in_z LPCWSTR wzPath
39 );
40
41/*******************************************************************
42 PathGetDirectory - extracts the directory from a path.
43********************************************************************/
44DAPI_(HRESULT) PathGetDirectory(
45 __in_z LPCWSTR wzPath,
46 __out_z LPWSTR *psczDirectory
47 );
48
49/*******************************************************************
50PathGetParentPath - extracts the parent directory from a full path.
51********************************************************************/
52DAPI_(HRESULT) PathGetParentPath(
53 __in_z LPCWSTR wzPath,
54 __out_z LPWSTR *psczDirectory
55 );
56
57/*******************************************************************
58 PathExpand - gets the full path to a file resolving environment
59 variables along the way.
60********************************************************************/
61DAPI_(HRESULT) PathExpand(
62 __out LPWSTR *psczFullPath,
63 __in_z LPCWSTR wzRelativePath,
64 __in DWORD dwResolveFlags
65 );
66
67/*******************************************************************
68 PathPrefix - prefixes a full path with \\?\ or \\?\UNC as
69 appropriate.
70********************************************************************/
71DAPI_(HRESULT) PathPrefix(
72 __inout LPWSTR *psczFullPath
73 );
74
75/*******************************************************************
76 PathFixedBackslashTerminate - appends a \ if path does not have it
77 already, but fails if the buffer is
78 insufficient.
79********************************************************************/
80DAPI_(HRESULT) PathFixedBackslashTerminate(
81 __inout_ecount_z(cchPath) LPWSTR wzPath,
82 __in SIZE_T cchPath
83 );
84
85/*******************************************************************
86 PathBackslashTerminate - appends a \ if path does not have it
87 already.
88********************************************************************/
89DAPI_(HRESULT) PathBackslashTerminate(
90 __inout LPWSTR* psczPath
91 );
92
93/*******************************************************************
94 PathForCurrentProcess - gets the full path to the currently executing
95 process or (optionally) a module inside the process.
96********************************************************************/
97DAPI_(HRESULT) PathForCurrentProcess(
98 __inout LPWSTR *psczFullPath,
99 __in_opt HMODULE hModule
100 );
101
102/*******************************************************************
103 PathRelativeToModule - gets the name of a file in the same
104 directory as the current process or (optionally) a module inside
105 the process
106********************************************************************/
107DAPI_(HRESULT) PathRelativeToModule(
108 __inout LPWSTR *psczFullPath,
109 __in_opt LPCWSTR wzFileName,
110 __in_opt HMODULE hModule
111 );
112
113/*******************************************************************
114 PathCreateTempFile
115
116 Note: if wzDirectory is null, ::GetTempPath() will be used instead.
117 if wzFileNameTemplate is null, GetTempFileName() will be used instead.
118*******************************************************************/
119DAPI_(HRESULT) PathCreateTempFile(
120 __in_opt LPCWSTR wzDirectory,
121 __in_opt __format_string LPCWSTR wzFileNameTemplate,
122 __in DWORD dwUniqueCount,
123 __in DWORD dwFileAttributes,
124 __out_opt LPWSTR* psczTempFile,
125 __out_opt HANDLE* phTempFile
126 );
127
128/*******************************************************************
129 PathCreateTimeBasedTempFile - creates an empty temp file based on current
130 system time
131********************************************************************/
132DAPI_(HRESULT) PathCreateTimeBasedTempFile(
133 __in_z_opt LPCWSTR wzDirectory,
134 __in_z LPCWSTR wzPrefix,
135 __in_z_opt LPCWSTR wzPostfix,
136 __in_z LPCWSTR wzExtension,
137 __deref_opt_out_z LPWSTR* psczTempFile,
138 __out_opt HANDLE* phTempFile
139 );
140
141/*******************************************************************
142 PathCreateTempDirectory
143
144 Note: if wzDirectory is null, ::GetTempPath() will be used instead.
145*******************************************************************/
146DAPI_(HRESULT) PathCreateTempDirectory(
147 __in_opt LPCWSTR wzDirectory,
148 __in __format_string LPCWSTR wzDirectoryNameTemplate,
149 __in DWORD dwUniqueCount,
150 __out LPWSTR* psczTempDirectory
151 );
152
153/*******************************************************************
154 PathGetKnownFolder - returns the path to a well-known shell folder
155
156*******************************************************************/
157DAPI_(HRESULT) PathGetKnownFolder(
158 __in int csidl,
159 __out LPWSTR* psczKnownFolder
160 );
161
162/*******************************************************************
163 PathIsAbsolute - returns true if the path is absolute; false
164 otherwise.
165*******************************************************************/
166DAPI_(BOOL) PathIsAbsolute(
167 __in_z LPCWSTR wzPath
168 );
169
170/*******************************************************************
171 PathConcat - like .NET's Path.Combine, lets you build up a path
172 one piece -- file or directory -- at a time.
173*******************************************************************/
174DAPI_(HRESULT) PathConcat(
175 __in_opt LPCWSTR wzPath1,
176 __in_opt LPCWSTR wzPath2,
177 __deref_out_z LPWSTR* psczCombined
178 );
179
180/*******************************************************************
181 PathConcatCch - like .NET's Path.Combine, lets you build up a path
182 one piece -- file or directory -- at a time.
183*******************************************************************/
184DAPI_(HRESULT) PathConcatCch(
185 __in_opt LPCWSTR wzPath1,
186 __in SIZE_T cchPath1,
187 __in_opt LPCWSTR wzPath2,
188 __in SIZE_T cchPath2,
189 __deref_out_z LPWSTR* psczCombined
190 );
191
192/*******************************************************************
193 PathEnsureQuoted - ensures that a path is quoted; optionally,
194 this function also terminates a directory with a backslash
195 if it is not already.
196*******************************************************************/
197DAPI_(HRESULT) PathEnsureQuoted(
198 __inout LPWSTR* ppszPath,
199 __in BOOL fDirectory
200 );
201
202/*******************************************************************
203 PathCompare - compares the fully expanded path of the two paths using
204 ::CompareStringW().
205*******************************************************************/
206DAPI_(HRESULT) PathCompare(
207 __in_z LPCWSTR wzPath1,
208 __in_z LPCWSTR wzPath2,
209 __out int* pnResult
210 );
211
212/*******************************************************************
213 PathCompress - sets the compression state on an existing file or
214 directory. A no-op on file systems that don't
215 support compression.
216*******************************************************************/
217DAPI_(HRESULT) PathCompress(
218 __in_z LPCWSTR wzPath
219 );
220
221/*******************************************************************
222 PathGetHierarchyArray - allocates an array containing,
223 in order, every parent directory of the specified path,
224 ending with the actual input path
225 This function also works with registry subkeys
226*******************************************************************/
227DAPI_(HRESULT) PathGetHierarchyArray(
228 __in_z LPCWSTR wzPath,
229 __deref_inout_ecount_opt(*pcPathArray) LPWSTR **prgsczPathArray,
230 __inout LPUINT pcPathArray
231 );
232
233/*******************************************************************
234 PathCanonicalizePath - wrapper around PathCanonicalizeW.
235*******************************************************************/
236DAPI_(HRESULT) PathCanonicalizePath(
237 __in_z LPCWSTR wzPath,
238 __deref_out_z LPWSTR* psczCanonicalized
239 );
240
241/*******************************************************************
242PathDirectoryContainsPath - checks if wzPath is located inside
243 wzDirectory.
244*******************************************************************/
245DAPI_(HRESULT) PathDirectoryContainsPath(
246 __in_z LPCWSTR wzDirectory,
247 __in_z LPCWSTR wzPath
248 );
249
250#ifdef __cplusplus
251}
252#endif
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/perfutil.h b/src/libs/dutil/WixToolset.DUtil/inc/perfutil.h
new file mode 100644
index 00000000..7557511d
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/perfutil.h
@@ -0,0 +1,24 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9// structs
10
11
12// functions
13void DAPI PerfInitialize(
14 );
15void DAPI PerfClickTime(
16 __out_opt LARGE_INTEGER* pliElapsed
17 );
18double DAPI PerfConvertToSeconds(
19 __in const LARGE_INTEGER* pli
20 );
21
22#ifdef __cplusplus
23}
24#endif
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/polcutil.h b/src/libs/dutil/WixToolset.DUtil/inc/polcutil.h
new file mode 100644
index 00000000..13618043
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/polcutil.h
@@ -0,0 +1,39 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9const LPCWSTR POLICY_BURN_REGISTRY_PATH = L"WiX\\Burn";
10
11/********************************************************************
12PolcReadNumber - reads a number from policy.
13
14NOTE: S_FALSE returned if policy not set.
15NOTE: out is set to default on S_FALSE or any error.
16********************************************************************/
17HRESULT DAPI PolcReadNumber(
18 __in_z LPCWSTR wzPolicyPath,
19 __in_z LPCWSTR wzPolicyName,
20 __in DWORD dwDefault,
21 __out DWORD* pdw
22 );
23
24/********************************************************************
25PolcReadString - reads a string from policy.
26
27NOTE: S_FALSE returned if policy not set.
28NOTE: out is set to default on S_FALSE or any error.
29********************************************************************/
30HRESULT DAPI PolcReadString(
31 __in_z LPCWSTR wzPolicyPath,
32 __in_z LPCWSTR wzPolicyName,
33 __in_z_opt LPCWSTR wzDefault,
34 __deref_out_z LPWSTR* pscz
35 );
36
37#ifdef __cplusplus
38}
39#endif
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/procutil.h b/src/libs/dutil/WixToolset.DUtil/inc/procutil.h
new file mode 100644
index 00000000..00f3f358
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/procutil.h
@@ -0,0 +1,75 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9// structs
10typedef struct _PROC_FILESYSTEMREDIRECTION
11{
12 BOOL fDisabled;
13 LPVOID pvRevertState;
14} PROC_FILESYSTEMREDIRECTION;
15
16HRESULT DAPI ProcElevated(
17 __in HANDLE hProcess,
18 __out BOOL* pfElevated
19 );
20
21HRESULT DAPI ProcWow64(
22 __in HANDLE hProcess,
23 __out BOOL* pfWow64
24 );
25HRESULT DAPI ProcDisableWowFileSystemRedirection(
26 __in PROC_FILESYSTEMREDIRECTION* pfsr
27 );
28HRESULT DAPI ProcRevertWowFileSystemRedirection(
29 __in PROC_FILESYSTEMREDIRECTION* pfsr
30 );
31
32HRESULT DAPI ProcExec(
33 __in_z LPCWSTR wzExecutablePath,
34 __in_z_opt LPCWSTR wzCommandLine,
35 __in int nCmdShow,
36 __out HANDLE *phProcess
37 );
38HRESULT DAPI ProcExecute(
39 __in_z LPWSTR wzCommand,
40 __out HANDLE *phProcess,
41 __out_opt HANDLE *phChildStdIn,
42 __out_opt HANDLE *phChildStdOutErr
43 );
44HRESULT DAPI ProcWaitForCompletion(
45 __in HANDLE hProcess,
46 __in DWORD dwTimeout,
47 __out DWORD *pReturnCode
48 );
49HRESULT DAPI ProcWaitForIds(
50 __in_ecount(cProcessIds) const DWORD* pdwProcessIds,
51 __in DWORD cProcessIds,
52 __in DWORD dwMilliseconds
53 );
54HRESULT DAPI ProcCloseIds(
55 __in_ecount(cProcessIds) const DWORD* pdwProcessIds,
56 __in DWORD cProcessIds
57 );
58
59// following code in proc2utl.cpp due to dependency on PSAPI.DLL.
60HRESULT DAPI ProcFindAllIdsFromExeName(
61 __in_z LPCWSTR wzExeName,
62 __out DWORD** ppdwProcessIds,
63 __out DWORD* pcProcessIds
64 );
65
66// following code in proc3utl.cpp due to dependency on Wtsapi32.DLL.
67HRESULT DAPI ProcExecuteAsInteractiveUser(
68 __in_z LPCWSTR wzExecutablePath,
69 __in_z LPCWSTR wzCommand,
70 __out HANDLE *phProcess
71 );
72
73#ifdef __cplusplus
74}
75#endif
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/regutil.h b/src/libs/dutil/WixToolset.DUtil/inc/regutil.h
new file mode 100644
index 00000000..75284940
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/regutil.h
@@ -0,0 +1,246 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9
10#define ReleaseRegKey(h) if (h) { ::RegCloseKey(h); h = NULL; }
11
12typedef enum REG_KEY_BITNESS
13{
14 REG_KEY_DEFAULT = 0,
15 REG_KEY_32BIT = 1,
16 REG_KEY_64BIT = 2
17} REG_KEY_BITNESS;
18
19typedef LSTATUS (APIENTRY *PFN_REGCREATEKEYEXW)(
20 __in HKEY hKey,
21 __in LPCWSTR lpSubKey,
22 __reserved DWORD Reserved,
23 __in_opt LPWSTR lpClass,
24 __in DWORD dwOptions,
25 __in REGSAM samDesired,
26 __in_opt CONST LPSECURITY_ATTRIBUTES lpSecurityAttributes,
27 __out PHKEY phkResult,
28 __out_opt LPDWORD lpdwDisposition
29 );
30typedef LSTATUS (APIENTRY *PFN_REGOPENKEYEXW)(
31 __in HKEY hKey,
32 __in_opt LPCWSTR lpSubKey,
33 __reserved DWORD ulOptions,
34 __in REGSAM samDesired,
35 __out PHKEY phkResult
36 );
37typedef LSTATUS (APIENTRY *PFN_REGDELETEKEYEXW)(
38 __in HKEY hKey,
39 __in LPCWSTR lpSubKey,
40 __in REGSAM samDesired,
41 __reserved DWORD Reserved
42 );
43typedef LSTATUS (APIENTRY *PFN_REGDELETEKEYW)(
44 __in HKEY hKey,
45 __in LPCWSTR lpSubKey
46 );
47typedef LSTATUS (APIENTRY *PFN_REGENUMKEYEXW)(
48 __in HKEY hKey,
49 __in DWORD dwIndex,
50 __out LPWSTR lpName,
51 __inout LPDWORD lpcName,
52 __reserved LPDWORD lpReserved,
53 __inout_opt LPWSTR lpClass,
54 __inout_opt LPDWORD lpcClass,
55 __out_opt PFILETIME lpftLastWriteTime
56 );
57typedef LSTATUS (APIENTRY *PFN_REGENUMVALUEW)(
58 __in HKEY hKey,
59 __in DWORD dwIndex,
60 __out LPWSTR lpValueName,
61 __inout LPDWORD lpcchValueName,
62 __reserved LPDWORD lpReserved,
63 __out_opt LPDWORD lpType,
64 __out_opt LPBYTE lpData,
65 __out_opt LPDWORD lpcbData
66 );
67typedef LSTATUS (APIENTRY *PFN_REGQUERYINFOKEYW)(
68 __in HKEY hKey,
69 __out_opt LPWSTR lpClass,
70 __inout_opt LPDWORD lpcClass,
71 __reserved LPDWORD lpReserved,
72 __out_opt LPDWORD lpcSubKeys,
73 __out_opt LPDWORD lpcMaxSubKeyLen,
74 __out_opt LPDWORD lpcMaxClassLen,
75 __out_opt LPDWORD lpcValues,
76 __out_opt LPDWORD lpcMaxValueNameLen,
77 __out_opt LPDWORD lpcMaxValueLen,
78 __out_opt LPDWORD lpcbSecurityDescriptor,
79 __out_opt PFILETIME lpftLastWriteTime
80 );
81typedef LSTATUS (APIENTRY *PFN_REGQUERYVALUEEXW)(
82 __in HKEY hKey,
83 __in_opt LPCWSTR lpValueName,
84 __reserved LPDWORD lpReserved,
85 __out_opt LPDWORD lpType,
86 __out_bcount_part_opt(*lpcbData, *lpcbData) __out_data_source(REGISTRY) LPBYTE lpData,
87 __inout_opt LPDWORD lpcbData
88 );
89typedef LSTATUS (APIENTRY *PFN_REGSETVALUEEXW)(
90 __in HKEY hKey,
91 __in_opt LPCWSTR lpValueName,
92 __reserved DWORD Reserved,
93 __in DWORD dwType,
94 __in_bcount_opt(cbData) CONST BYTE* lpData,
95 __in DWORD cbData
96 );
97typedef LSTATUS (APIENTRY *PFN_REGDELETEVALUEW)(
98 __in HKEY hKey,
99 __in_opt LPCWSTR lpValueName
100 );
101
102HRESULT DAPI RegInitialize();
103void DAPI RegUninitialize();
104
105void DAPI RegFunctionOverride(
106 __in_opt PFN_REGCREATEKEYEXW pfnRegCreateKeyExW,
107 __in_opt PFN_REGOPENKEYEXW pfnRegOpenKeyExW,
108 __in_opt PFN_REGDELETEKEYEXW pfnRegDeleteKeyExW,
109 __in_opt PFN_REGENUMKEYEXW pfnRegEnumKeyExW,
110 __in_opt PFN_REGENUMVALUEW pfnRegEnumValueW,
111 __in_opt PFN_REGQUERYINFOKEYW pfnRegQueryInfoKeyW,
112 __in_opt PFN_REGQUERYVALUEEXW pfnRegQueryValueExW,
113 __in_opt PFN_REGSETVALUEEXW pfnRegSetValueExW,
114 __in_opt PFN_REGDELETEVALUEW pfnRegDeleteValueW
115 );
116HRESULT DAPI RegCreate(
117 __in HKEY hkRoot,
118 __in_z LPCWSTR wzSubKey,
119 __in DWORD dwAccess,
120 __out HKEY* phk
121 );
122HRESULT DAPI RegCreateEx(
123 __in HKEY hkRoot,
124 __in_z LPCWSTR wzSubKey,
125 __in DWORD dwAccess,
126 __in BOOL fVolatile,
127 __in_opt SECURITY_ATTRIBUTES* pSecurityAttributes,
128 __out HKEY* phk,
129 __out_opt BOOL* pfCreated
130 );
131HRESULT DAPI RegOpen(
132 __in HKEY hkRoot,
133 __in_z LPCWSTR wzSubKey,
134 __in DWORD dwAccess,
135 __out HKEY* phk
136 );
137HRESULT DAPI RegDelete(
138 __in HKEY hkRoot,
139 __in_z LPCWSTR wzSubKey,
140 __in REG_KEY_BITNESS kbKeyBitness,
141 __in BOOL fDeleteTree
142 );
143HRESULT DAPI RegKeyEnum(
144 __in HKEY hk,
145 __in DWORD dwIndex,
146 __deref_out_z LPWSTR* psczKey
147 );
148HRESULT DAPI RegValueEnum(
149 __in HKEY hk,
150 __in DWORD dwIndex,
151 __deref_out_z LPWSTR* psczName,
152 __out_opt DWORD *pdwType
153 );
154HRESULT DAPI RegGetType(
155 __in HKEY hk,
156 __in_z_opt LPCWSTR wzName,
157 __out DWORD *pdwType
158 );
159HRESULT DAPI RegReadBinary(
160 __in HKEY hk,
161 __in_z_opt LPCWSTR wzName,
162 __deref_out_bcount_opt(*pcbBuffer) BYTE** ppbBuffer,
163 __out SIZE_T *pcbBuffer
164 );
165HRESULT DAPI RegReadString(
166 __in HKEY hk,
167 __in_z_opt LPCWSTR wzName,
168 __deref_out_z LPWSTR* psczValue
169 );
170HRESULT DAPI RegReadStringArray(
171 __in HKEY hk,
172 __in_z_opt LPCWSTR wzName,
173 __deref_out_ecount_opt(*pcStrings) LPWSTR** prgsczStrings,
174 __out DWORD *pcStrings
175 );
176HRESULT DAPI RegReadVersion(
177 __in HKEY hk,
178 __in_z_opt LPCWSTR wzName,
179 __out DWORD64* pdw64Version
180 );
181HRESULT DAPI RegReadNumber(
182 __in HKEY hk,
183 __in_z_opt LPCWSTR wzName,
184 __out DWORD* pdwValue
185 );
186HRESULT DAPI RegReadQword(
187 __in HKEY hk,
188 __in_z_opt LPCWSTR wzName,
189 __out DWORD64* pqwValue
190 );
191HRESULT DAPI RegWriteBinary(
192 __in HKEY hk,
193 __in_z_opt LPCWSTR wzName,
194 __in_bcount(cbBuffer) const BYTE *pbBuffer,
195 __in DWORD cbBuffer
196 );
197HRESULT DAPI RegWriteString(
198 __in HKEY hk,
199 __in_z_opt LPCWSTR wzName,
200 __in_z_opt LPCWSTR wzValue
201 );
202HRESULT DAPI RegWriteStringArray(
203 __in HKEY hk,
204 __in_z_opt LPCWSTR wzName,
205 __in_ecount(cStrings) LPWSTR *rgwzStrings,
206 __in DWORD cStrings
207 );
208HRESULT DAPI RegWriteStringFormatted(
209 __in HKEY hk,
210 __in_z_opt LPCWSTR wzName,
211 __in __format_string LPCWSTR szFormat,
212 ...
213 );
214HRESULT DAPI RegWriteNumber(
215 __in HKEY hk,
216 __in_z_opt LPCWSTR wzName,
217 __in DWORD dwValue
218 );
219HRESULT DAPI RegWriteQword(
220 __in HKEY hk,
221 __in_z_opt LPCWSTR wzName,
222 __in DWORD64 qwValue
223 );
224HRESULT DAPI RegQueryKey(
225 __in HKEY hk,
226 __out_opt DWORD* pcSubKeys,
227 __out_opt DWORD* pcValues
228 );
229HRESULT DAPI RegKeyReadNumber(
230 __in HKEY hk,
231 __in_z LPCWSTR wzSubKey,
232 __in_z_opt LPCWSTR wzName,
233 __in BOOL f64Bit,
234 __out DWORD* pdwValue
235 );
236BOOL DAPI RegValueExists(
237 __in HKEY hk,
238 __in_z LPCWSTR wzSubKey,
239 __in_z_opt LPCWSTR wzName,
240 __in BOOL f64Bit
241 );
242
243#ifdef __cplusplus
244}
245#endif
246
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/resrutil.h b/src/libs/dutil/WixToolset.DUtil/inc/resrutil.h
new file mode 100644
index 00000000..1f4d8e17
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/resrutil.h
@@ -0,0 +1,43 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9HRESULT DAPI ResGetStringLangId(
10 __in_opt LPCWSTR wzPath,
11 __in UINT uID,
12 __out WORD *pwLangId
13 );
14
15HRESULT DAPI ResReadString(
16 __in HINSTANCE hinst,
17 __in UINT uID,
18 __deref_out_z LPWSTR* ppwzString
19 );
20
21HRESULT DAPI ResReadStringAnsi(
22 __in HINSTANCE hinst,
23 __in UINT uID,
24 __deref_out_z LPSTR* ppszString
25 );
26
27HRESULT DAPI ResReadData(
28 __in_opt HINSTANCE hinst,
29 __in_z LPCSTR szDataName,
30 __deref_out_bcount(*pcb) PVOID *ppv,
31 __out DWORD *pcb
32 );
33
34HRESULT DAPI ResExportDataToFile(
35 __in_z LPCSTR szDataName,
36 __in_z LPCWSTR wzTargetFile,
37 __in DWORD dwCreationDisposition
38 );
39
40#ifdef __cplusplus
41}
42#endif
43
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/reswutil.h b/src/libs/dutil/WixToolset.DUtil/inc/reswutil.h
new file mode 100644
index 00000000..31435ae2
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/reswutil.h
@@ -0,0 +1,31 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9HRESULT DAPI ResWriteString(
10 __in_z LPCWSTR wzResourceFile,
11 __in DWORD dwDataId,
12 __in_z LPCWSTR wzData,
13 __in WORD wLangId
14 );
15
16HRESULT DAPI ResWriteData(
17 __in_z LPCWSTR wzResourceFile,
18 __in_z LPCSTR szDataName,
19 __in PVOID pData,
20 __in DWORD cbData
21 );
22
23HRESULT DAPI ResImportDataFromFile(
24 __in_z LPCWSTR wzTargetFile,
25 __in_z LPCWSTR wzSourceFile,
26 __in_z LPCSTR szDataName
27 );
28
29#ifdef __cplusplus
30}
31#endif
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/rexutil.h b/src/libs/dutil/WixToolset.DUtil/inc/rexutil.h
new file mode 100644
index 00000000..77f5604a
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/rexutil.h
@@ -0,0 +1,54 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#include <sys\stat.h>
6#include <fdi.h>
7
8#ifdef __cplusplus
9extern "C" {
10#endif
11
12// defines
13#define FILETABLESIZE 40
14
15// structs
16struct MEM_FILE
17{
18 LPCBYTE vpStart;
19 UINT uiCurrent;
20 UINT uiLength;
21};
22
23typedef enum FAKE_FILE_TYPE { NORMAL_FILE, MEMORY_FILE } FAKE_FILE_TYPE;
24
25typedef HRESULT (*REX_CALLBACK_PROGRESS)(BOOL fBeginFile, LPCWSTR wzFileId, LPVOID pvContext);
26typedef VOID (*REX_CALLBACK_WRITE)(UINT cb);
27
28
29struct FAKE_FILE // used __in internal file table
30{
31 BOOL fUsed;
32 FAKE_FILE_TYPE fftType;
33 MEM_FILE mfFile; // State for memory file
34 HANDLE hFile; // Handle for disk file
35};
36
37// functions
38HRESULT RexInitialize();
39void RexUninitialize();
40
41HRESULT RexExtract(
42 __in_z LPCSTR szResource,
43 __in_z LPCWSTR wzExtractId,
44 __in_z LPCWSTR wzExtractDir,
45 __in_z LPCWSTR wzExtractName,
46 __in REX_CALLBACK_PROGRESS pfnProgress,
47 __in REX_CALLBACK_WRITE pfnWrite,
48 __in LPVOID pvContext
49 );
50
51#ifdef __cplusplus
52}
53#endif
54
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/rmutil.h b/src/libs/dutil/WixToolset.DUtil/inc/rmutil.h
new file mode 100644
index 00000000..ce7bf254
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/rmutil.h
@@ -0,0 +1,46 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9typedef struct _RMU_SESSION *PRMU_SESSION;
10
11HRESULT DAPI RmuJoinSession(
12 __out PRMU_SESSION *ppSession,
13 __in_z LPCWSTR wzSessionKey
14 );
15
16HRESULT DAPI RmuAddFile(
17 __in PRMU_SESSION pSession,
18 __in_z LPCWSTR wzPath
19 );
20
21HRESULT DAPI RmuAddProcessById(
22 __in PRMU_SESSION pSession,
23 __in DWORD dwProcessId
24 );
25
26HRESULT DAPI RmuAddProcessesByName(
27 __in PRMU_SESSION pSession,
28 __in_z LPCWSTR wzProcessName
29 );
30
31HRESULT DAPI RmuAddService(
32 __in PRMU_SESSION pSession,
33 __in_z LPCWSTR wzServiceName
34 );
35
36HRESULT DAPI RmuRegisterResources(
37 __in PRMU_SESSION pSession
38 );
39
40HRESULT DAPI RmuEndSession(
41 __in PRMU_SESSION pSession
42 );
43
44#ifdef __cplusplus
45}
46#endif
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/rssutil.h b/src/libs/dutil/WixToolset.DUtil/inc/rssutil.h
new file mode 100644
index 00000000..064ab147
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/rssutil.h
@@ -0,0 +1,89 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9#define ReleaseRssChannel(p) if (p) { RssFreeChannel(p); }
10#define ReleaseNullRssChannel(p) if (p) { RssFreeChannel(p); p = NULL; }
11
12
13struct RSS_UNKNOWN_ATTRIBUTE
14{
15 LPWSTR wzNamespace;
16 LPWSTR wzAttribute;
17 LPWSTR wzValue;
18
19 RSS_UNKNOWN_ATTRIBUTE* pNext;
20};
21
22struct RSS_UNKNOWN_ELEMENT
23{
24 LPWSTR wzNamespace;
25 LPWSTR wzElement;
26 LPWSTR wzValue;
27
28 RSS_UNKNOWN_ATTRIBUTE* pAttributes;
29 RSS_UNKNOWN_ELEMENT* pNext;
30};
31
32struct RSS_ITEM
33{
34 LPWSTR wzTitle;
35 LPWSTR wzLink;
36 LPWSTR wzDescription;
37
38 LPWSTR wzGuid;
39 FILETIME ftPublished;
40
41 LPWSTR wzEnclosureUrl;
42 DWORD dwEnclosureSize;
43 LPWSTR wzEnclosureType;
44
45 RSS_UNKNOWN_ELEMENT* pUnknownElements;
46};
47
48struct RSS_CHANNEL
49{
50 LPWSTR wzTitle;
51 LPWSTR wzLink;
52 LPWSTR wzDescription;
53 DWORD dwTimeToLive;
54
55 RSS_UNKNOWN_ELEMENT* pUnknownElements;
56
57 DWORD cItems;
58 RSS_ITEM rgItems[1];
59};
60
61HRESULT DAPI RssInitialize(
62 );
63
64void DAPI RssUninitialize(
65 );
66
67HRESULT DAPI RssParseFromString(
68 __in_z LPCWSTR wzRssString,
69 __out RSS_CHANNEL **ppChannel
70 );
71
72HRESULT DAPI RssParseFromFile(
73 __in_z LPCWSTR wzRssFile,
74 __out RSS_CHANNEL **ppChannel
75 );
76
77// Adding this until we have the updated specstrings.h
78#ifndef __in_xcount
79#define __in_xcount(size)
80#endif
81
82void DAPI RssFreeChannel(
83 __in_xcount(pChannel->cItems) RSS_CHANNEL *pChannel
84 );
85
86#ifdef __cplusplus
87}
88#endif
89
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/sceutil.h b/src/libs/dutil/WixToolset.DUtil/inc/sceutil.h
new file mode 100644
index 00000000..9d14eecf
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/sceutil.h
@@ -0,0 +1,273 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9#include <sqlce_oledb.h>
10#include <sqlce_sync.h>
11#include <sqlce_err.h>
12
13typedef void* SCE_DATABASE_HANDLE;
14typedef void* SCE_ROW_HANDLE;
15typedef void* SCE_QUERY_HANDLE;
16typedef void* SCE_QUERY_RESULTS_HANDLE;
17
18extern const int SCE_ROW_HANDLE_BYTES;
19extern const int SCE_QUERY_HANDLE_BYTES;
20extern const int SCE_QUERY_RESULTS_HANDLE_BYTES;
21
22#define ReleaseSceRow(prrh) if (prrh) { SceFreeRow(prrh); }
23#define ReleaseNullSceRow(prrh) if (prrh) { SceFreeRow(prrh); prrh = NULL; }
24#define ReleaseSceQuery(pqh) if (pqh) { SceFreeQuery(pqh); }
25#define ReleaseNullSceQuery(pqh) if (pqh) { SceFreeQuery(pqh); pqh = NULL; }
26#define ReleaseSceQueryResults(pqh) if (pqh) { SceFreeQueryResults(pqh); }
27#define ReleaseNullSceQueryResults(pqh) if (pqh) { SceFreeQueryResults(pqh); pqh = NULL; }
28
29struct SCE_COLUMN_SCHEMA
30{
31 LPCWSTR wzName;
32 DBTYPE dbtColumnType;
33 DWORD dwLength;
34 BOOL fPrimaryKey; // If this column is the primary key
35 BOOL fNullable;
36 BOOL fAutoIncrement;
37 BOOL fDescending; // If this column should be descending when used in an index (default is ascending)
38
39 LPWSTR wzRelationName;
40 DWORD dwForeignKeyTable;
41 DWORD dwForeignKeyColumn;
42};
43
44struct SCE_INDEX_SCHEMA
45{
46 LPWSTR wzName;
47
48 DWORD *rgColumns;
49 DWORD cColumns;
50};
51
52struct SCE_TABLE_SCHEMA
53{
54 LPCWSTR wzName;
55 DWORD cColumns;
56 SCE_COLUMN_SCHEMA *rgColumns;
57
58 DWORD cIndexes;
59 SCE_INDEX_SCHEMA *rgIndexes;
60
61 // Internal to SCEUtil - consumers shouldn't access or modify
62 // TODO: enforce / hide in a handle of some sort?
63 IRowset *pIRowset;
64 IRowsetChange *pIRowsetChange;
65};
66
67struct SCE_DATABASE_SCHEMA
68{
69 DWORD cTables;
70 SCE_TABLE_SCHEMA *rgTables;
71};
72
73struct SCE_DATABASE
74{
75 SCE_DATABASE_HANDLE sdbHandle;
76 SCE_DATABASE_SCHEMA *pdsSchema;
77};
78
79HRESULT DAPI SceCreateDatabase(
80 __in_z LPCWSTR sczFile,
81 __in_z_opt LPCWSTR wzSqlCeDllPath,
82 __deref_out SCE_DATABASE **ppDatabase
83 );
84HRESULT DAPI SceOpenDatabase(
85 __in_z LPCWSTR sczFile,
86 __in_z_opt LPCWSTR wzSqlCeDllPath,
87 __in LPCWSTR wzSchemaType,
88 __in DWORD dwExpectedVersion,
89 __deref_out SCE_DATABASE **ppDatabase,
90 __in BOOL fReadOnly
91 );
92HRESULT DAPI SceEnsureDatabase(
93 __in_z LPCWSTR sczFile,
94 __in_z_opt LPCWSTR wzSqlCeDllPath,
95 __in LPCWSTR wzSchemaType,
96 __in DWORD dwExpectedVersion,
97 __in SCE_DATABASE_SCHEMA *pdsSchema,
98 __deref_out SCE_DATABASE **ppDatabase
99 );
100HRESULT DAPI SceIsTableEmpty(
101 __in SCE_DATABASE *pDatabase,
102 __in DWORD dwTableIndex,
103 __out BOOL *pfEmpty
104 );
105HRESULT DAPI SceGetFirstRow(
106 __in SCE_DATABASE *pDatabase,
107 __in DWORD dwTableIndex,
108 __deref_out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle
109 );
110HRESULT DAPI SceGetNextRow(
111 __in SCE_DATABASE *pDatabase,
112 __in DWORD dwTableIndex,
113 __deref_out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle
114 );
115HRESULT DAPI SceBeginTransaction(
116 __in SCE_DATABASE *pDatabase
117 );
118HRESULT DAPI SceCommitTransaction(
119 __in SCE_DATABASE *pDatabase
120 );
121HRESULT DAPI SceRollbackTransaction(
122 __in SCE_DATABASE *pDatabase
123 );
124HRESULT DAPI SceDeleteRow(
125 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle
126 );
127HRESULT DAPI ScePrepareInsert(
128 __in SCE_DATABASE *pDatabase,
129 __in DWORD dwTableIndex,
130 __deref_out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle
131 );
132HRESULT DAPI SceFinishUpdate(
133 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle
134 );
135HRESULT DAPI SceSetColumnBinary(
136 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle,
137 __in DWORD dwColumnIndex,
138 __in_bcount(cbBuffer) const BYTE* pbBuffer,
139 __in SIZE_T cbBuffer
140 );
141HRESULT DAPI SceSetColumnDword(
142 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle,
143 __in DWORD dwColumnIndex,
144 __in const DWORD dwValue
145 );
146HRESULT DAPI SceSetColumnQword(
147 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle,
148 __in DWORD dwColumnIndex,
149 __in const DWORD64 qwValue
150 );
151HRESULT DAPI SceSetColumnBool(
152 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle,
153 __in DWORD dwColumnIndex,
154 __in const BOOL fValue
155 );
156HRESULT DAPI SceSetColumnString(
157 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle,
158 __in DWORD dwColumnIndex,
159 __in_z_opt LPCWSTR wzValue
160 );
161HRESULT DAPI SceSetColumnSystemTime(
162 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle,
163 __in DWORD dwColumnIndex,
164 __in const SYSTEMTIME *pst
165 );
166HRESULT DAPI SceSetColumnNull(
167 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle,
168 __in DWORD dwColumnIndex
169 );
170HRESULT DAPI SceGetColumnBinary(
171 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle,
172 __in DWORD dwColumnIndex,
173 __out_opt BYTE **ppbBuffer,
174 __inout SIZE_T *pcbBuffer
175 );
176HRESULT DAPI SceGetColumnDword(
177 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle,
178 __in DWORD dwColumnIndex,
179 __out DWORD *pdwValue
180 );
181HRESULT DAPI SceGetColumnQword(
182 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle,
183 __in DWORD dwColumnIndex,
184 __out DWORD64 *pqwValue
185 );
186HRESULT DAPI SceGetColumnBool(
187 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle,
188 __in DWORD dwColumnIndex,
189 __out BOOL *pfValue
190 );
191HRESULT DAPI SceGetColumnString(
192 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle,
193 __in DWORD dwColumnIndex,
194 __out_z LPWSTR *psczValue
195 );
196HRESULT DAPI SceGetColumnSystemTime(
197 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle,
198 __in DWORD dwColumnIndex,
199 __out SYSTEMTIME *pst
200 );
201HRESULT DAPI SceBeginQuery(
202 __in SCE_DATABASE *pDatabase,
203 __in DWORD dwTableIndex,
204 __in DWORD dwIndex,
205 __deref_out_bcount(SCE_QUERY_HANDLE_BYTES) SCE_QUERY_HANDLE *psqhHandle
206 );
207HRESULT DAPI SceSetQueryColumnBinary(
208 __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle,
209 __in_bcount(cbBuffer) const BYTE* pbBuffer,
210 __in SIZE_T cbBuffer
211 );
212HRESULT DAPI SceSetQueryColumnDword(
213 __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle,
214 __in const DWORD dwValue
215 );
216HRESULT DAPI SceSetQueryColumnQword(
217 __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle,
218 __in const DWORD64 qwValue
219 );
220HRESULT DAPI SceSetQueryColumnBool(
221 __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle,
222 __in const BOOL fValue
223 );
224HRESULT DAPI SceSetQueryColumnString(
225 __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle,
226 __in_z_opt LPCWSTR wzString
227 );
228HRESULT DAPI SceSetQueryColumnSystemTime(
229 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle,
230 __in const SYSTEMTIME *pst
231 );
232HRESULT DAPI SceSetQueryColumnEmpty(
233 __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle
234 );
235HRESULT DAPI SceRunQueryExact(
236 __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE *psqhHandle,
237 __deref_out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle
238 );
239HRESULT DAPI SceRunQueryRange(
240 __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE *psqhHandle,
241 __deref_out_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS_HANDLE *psqrhHandle
242 );
243HRESULT DAPI SceGetNextResultRow(
244 __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS_HANDLE sqrhHandle,
245 __deref_out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle
246 );
247void DAPI SceCloseTable(
248 __in SCE_TABLE_SCHEMA *pTable
249 );
250// Returns whether the data in the database changed. Ignores schema changes.
251BOOL DAPI SceDatabaseChanged(
252 __in SCE_DATABASE *pDatabase
253 );
254// Resets the database changed flag
255void DAPI SceResetDatabaseChanged(
256 __in SCE_DATABASE *pDatabase
257 );
258HRESULT DAPI SceCloseDatabase(
259 __in SCE_DATABASE *pDatabase
260 );
261void DAPI SceFreeRow(
262 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle
263 );
264void DAPI SceFreeQuery(
265 __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE sqhHandle
266 );
267void DAPI SceFreeQueryResults(
268 __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS_HANDLE sqrhHandle
269 );
270
271#ifdef __cplusplus
272}
273#endif
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/sczutil.h b/src/libs/dutil/WixToolset.DUtil/inc/sczutil.h
new file mode 100644
index 00000000..fcfbd13a
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/sczutil.h
@@ -0,0 +1,30 @@
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
6class PSCZ
7{
8public:
9 PSCZ() : m_scz(NULL) { }
10
11 ~PSCZ() { ReleaseNullStr(m_scz); }
12
13 operator LPWSTR() { return m_scz; }
14
15 operator LPCWSTR() { return m_scz; }
16
17 operator bool() { return NULL != m_scz; }
18
19 LPWSTR* operator &() { return &m_scz; }
20
21 bool operator !() { return !m_scz; }
22
23 WCHAR operator *() { return *m_scz; }
24
25 LPWSTR Detach() { LPWSTR scz = m_scz; m_scz = NULL; return scz; }
26
27private:
28 LPWSTR m_scz;
29};
30#endif //__cplusplus
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/shelutil.h b/src/libs/dutil/WixToolset.DUtil/inc/shelutil.h
new file mode 100644
index 00000000..0b9f539d
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/shelutil.h
@@ -0,0 +1,47 @@
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#ifndef REFKNOWNFOLDERID
6#define REFKNOWNFOLDERID REFGUID
7#endif
8
9#ifdef __cplusplus
10extern "C" {
11#endif
12
13typedef BOOL (STDAPICALLTYPE *PFN_SHELLEXECUTEEXW)(
14 __inout LPSHELLEXECUTEINFOW lpExecInfo
15 );
16
17void DAPI ShelFunctionOverride(
18 __in_opt PFN_SHELLEXECUTEEXW pfnShellExecuteExW
19 );
20HRESULT DAPI ShelExec(
21 __in_z LPCWSTR wzTargetPath,
22 __in_z_opt LPCWSTR wzParameters,
23 __in_z_opt LPCWSTR wzVerb,
24 __in_z_opt LPCWSTR wzWorkingDirectory,
25 __in int nShowCmd,
26 __in_opt HWND hwndParent,
27 __out_opt HANDLE* phProcess
28 );
29HRESULT DAPI ShelExecUnelevated(
30 __in_z LPCWSTR wzTargetPath,
31 __in_z_opt LPCWSTR wzParameters,
32 __in_z_opt LPCWSTR wzVerb,
33 __in_z_opt LPCWSTR wzWorkingDirectory,
34 __in int nShowCmd
35 );
36HRESULT DAPI ShelGetFolder(
37 __out_z LPWSTR* psczFolderPath,
38 __in int csidlFolder
39 );
40HRESULT DAPI ShelGetKnownFolder(
41 __out_z LPWSTR* psczFolderPath,
42 __in REFKNOWNFOLDERID rfidFolder
43 );
44
45#ifdef __cplusplus
46}
47#endif
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/sqlutil.h b/src/libs/dutil/WixToolset.DUtil/inc/sqlutil.h
new file mode 100644
index 00000000..ddf09323
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/sqlutil.h
@@ -0,0 +1,136 @@
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 <cguid.h>
6#include <oledberr.h>
7#include <sqloledb.h>
8
9
10#ifdef __cplusplus
11extern "C" {
12#endif
13
14// Adding this until the SQL annotations are published to specstrings.h
15#ifndef __sql_command
16#define __sql_command
17#endif
18
19// structs
20struct SQL_FILESPEC
21{
22 WCHAR wzName[MAX_PATH];
23 WCHAR wzFilename[MAX_PATH];
24 WCHAR wzSize[MAX_PATH];
25 WCHAR wzMaxSize[MAX_PATH];
26 WCHAR wzGrow[MAX_PATH];
27};
28
29
30// functions
31HRESULT DAPI SqlConnectDatabase(
32 __in_z LPCWSTR wzServer,
33 __in_z LPCWSTR wzInstance,
34 __in_z LPCWSTR wzDatabase,
35 __in BOOL fIntegratedAuth,
36 __in_z LPCWSTR wzUser,
37 __in_z LPCWSTR wzPassword,
38 __out IDBCreateSession** ppidbSession
39 );
40HRESULT DAPI SqlStartTransaction(
41 __in IDBCreateSession* pidbSession,
42 __out IDBCreateCommand** ppidbCommand,
43 __out ITransaction** ppit
44 );
45HRESULT DAPI SqlEndTransaction(
46 __in ITransaction* pit,
47 __in BOOL fCommit
48 );
49HRESULT DAPI SqlDatabaseExists(
50 __in_z LPCWSTR wzServer,
51 __in_z LPCWSTR wzInstance,
52 __in_z LPCWSTR wzDatabase,
53 __in BOOL fIntegratedAuth,
54 __in_z LPCWSTR wzUser,
55 __in_z LPCWSTR wzPassword,
56 __out_opt BSTR* pbstrErrorDescription
57 );
58HRESULT DAPI SqlSessionDatabaseExists(
59 __in IDBCreateSession* pidbSession,
60 __in_z LPCWSTR wzDatabase,
61 __out_opt BSTR* pbstrErrorDescription
62 );
63HRESULT DAPI SqlDatabaseEnsureExists(
64 __in_z LPCWSTR wzServer,
65 __in_z LPCWSTR wzInstance,
66 __in_z LPCWSTR wzDatabase,
67 __in BOOL fIntegratedAuth,
68 __in_z LPCWSTR wzUser,
69 __in_z LPCWSTR wzPassword,
70 __in_opt const SQL_FILESPEC* psfDatabase,
71 __in_opt const SQL_FILESPEC* psfLog,
72 __out_opt BSTR* pbstrErrorDescription
73 );
74HRESULT DAPI SqlSessionDatabaseEnsureExists(
75 __in IDBCreateSession* pidbSession,
76 __in_z LPCWSTR wzDatabase,
77 __in_opt const SQL_FILESPEC* psfDatabase,
78 __in_opt const SQL_FILESPEC* psfLog,
79 __out_opt BSTR* pbstrErrorDescription
80 );
81HRESULT DAPI SqlCreateDatabase(
82 __in_z LPCWSTR wzServer,
83 __in_z LPCWSTR wzInstance,
84 __in_z LPCWSTR wzDatabase,
85 __in BOOL fIntegratedAuth,
86 __in_z LPCWSTR wzUser,
87 __in_z LPCWSTR wzPassword,
88 __in_opt const SQL_FILESPEC* psfDatabase,
89 __in_opt const SQL_FILESPEC* psfLog,
90 __out_opt BSTR* pbstrErrorDescription
91 );
92HRESULT DAPI SqlSessionCreateDatabase(
93 __in IDBCreateSession* pidbSession,
94 __in_z LPCWSTR wzDatabase,
95 __in_opt const SQL_FILESPEC* psfDatabase,
96 __in_opt const SQL_FILESPEC* psfLog,
97 __out_opt BSTR* pbstrErrorDescription
98 );
99HRESULT DAPI SqlDropDatabase(
100 __in_z LPCWSTR wzServer,
101 __in_z LPCWSTR wzInstance,
102 __in_z LPCWSTR wzDatabase,
103 __in BOOL fIntegratedAuth,
104 __in_z LPCWSTR wzUser,
105 __in_z LPCWSTR wzPassword,
106 __out_opt BSTR* pbstrErrorDescription
107 );
108HRESULT DAPI SqlSessionDropDatabase(
109 __in IDBCreateSession* pidbSession,
110 __in_z LPCWSTR wzDatabase,
111 __out_opt BSTR* pbstrErrorDescription
112 );
113HRESULT DAPI SqlSessionExecuteQuery(
114 __in IDBCreateSession* pidbSession,
115 __in __sql_command LPCWSTR wzSql,
116 __out_opt IRowset** ppirs,
117 __out_opt DBROWCOUNT* pcRows,
118 __out_opt BSTR* pbstrErrorDescription
119 );
120HRESULT DAPI SqlCommandExecuteQuery(
121 __in IDBCreateCommand* pidbCommand,
122 __in __sql_command LPCWSTR wzSql,
123 __out IRowset** ppirs,
124 __out DBROWCOUNT* pcRows
125 );
126HRESULT DAPI SqlGetErrorInfo(
127 __in IUnknown* pObjectWithError,
128 __in REFIID IID_InterfaceWithError,
129 __in DWORD dwLocaleId,
130 __out_opt BSTR* pbstrErrorSource,
131 __out_opt BSTR* pbstrErrorDescription
132 );
133
134#ifdef __cplusplus
135}
136#endif
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/srputil.h b/src/libs/dutil/WixToolset.DUtil/inc/srputil.h
new file mode 100644
index 00000000..95e96231
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/srputil.h
@@ -0,0 +1,45 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9
10typedef enum SRP_ACTION
11{
12 SRP_ACTION_UNKNOWN,
13 SRP_ACTION_UNINSTALL,
14 SRP_ACTION_INSTALL,
15 SRP_ACTION_MODIFY,
16} SRP_ACTION;
17
18
19/********************************************************************
20 SrpInitialize - initializes system restore point functionality.
21
22*******************************************************************/
23DAPI_(HRESULT) SrpInitialize(
24 __in BOOL fInitializeComSecurity
25 );
26
27/********************************************************************
28 SrpUninitialize - uninitializes system restore point functionality.
29
30*******************************************************************/
31DAPI_(void) SrpUninitialize();
32
33/********************************************************************
34 SrpCreateRestorePoint - creates a system restore point.
35
36*******************************************************************/
37DAPI_(HRESULT) SrpCreateRestorePoint(
38 __in_z LPCWSTR wzApplicationName,
39 __in SRP_ACTION action
40 );
41
42#ifdef __cplusplus
43}
44#endif
45
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/strutil.h b/src/libs/dutil/WixToolset.DUtil/inc/strutil.h
new file mode 100644
index 00000000..1cff9ab8
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/strutil.h
@@ -0,0 +1,316 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9#define ReleaseStr(pwz) if (pwz) { StrFree(pwz); }
10#define ReleaseNullStr(pwz) if (pwz) { StrFree(pwz); pwz = NULL; }
11#define ReleaseBSTR(bstr) if (bstr) { ::SysFreeString(bstr); }
12#define ReleaseNullBSTR(bstr) if (bstr) { ::SysFreeString(bstr); bstr = NULL; }
13#define ReleaseStrArray(rg, c) { if (rg) { StrArrayFree(rg, c); } }
14#define ReleaseNullStrArray(rg, c) { if (rg) { StrArrayFree(rg, c); c = 0; rg = NULL; } }
15#define ReleaseNullStrSecure(pwz) if (pwz) { StrSecureZeroFreeString(pwz); pwz = NULL; }
16
17#define DeclareConstBSTR(bstr_const, wz) const WCHAR bstr_const[] = { 0x00, 0x00, sizeof(wz)-sizeof(WCHAR), 0x00, wz }
18#define UseConstBSTR(bstr_const) const_cast<BSTR>(bstr_const + 4)
19
20HRESULT DAPI StrAlloc(
21 __deref_out_ecount_part(cch, 0) LPWSTR* ppwz,
22 __in SIZE_T cch
23 );
24HRESULT DAPI StrAllocSecure(
25 __deref_out_ecount_part(cch, 0) LPWSTR* ppwz,
26 __in SIZE_T cch
27 );
28HRESULT DAPI StrTrimCapacity(
29 __deref_out_z LPWSTR* ppwz
30 );
31HRESULT DAPI StrTrimWhitespace(
32 __deref_out_z LPWSTR* ppwz,
33 __in_z LPCWSTR wzSource
34 );
35HRESULT DAPI StrAnsiAlloc(
36 __deref_out_ecount_part(cch, 0) LPSTR* ppz,
37 __in SIZE_T cch
38 );
39HRESULT DAPI StrAnsiTrimCapacity(
40 __deref_out_z LPSTR* ppz
41 );
42HRESULT DAPI StrAnsiTrimWhitespace(
43 __deref_out_z LPSTR* ppz,
44 __in_z LPCSTR szSource
45 );
46HRESULT DAPI StrAllocString(
47 __deref_out_ecount_z(cchSource+1) LPWSTR* ppwz,
48 __in_z LPCWSTR wzSource,
49 __in SIZE_T cchSource
50 );
51HRESULT DAPI StrAllocStringSecure(
52 __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz,
53 __in_z LPCWSTR wzSource,
54 __in SIZE_T cchSource
55 );
56HRESULT DAPI StrAnsiAllocString(
57 __deref_out_ecount_z(cchSource+1) LPSTR* ppsz,
58 __in_z LPCWSTR wzSource,
59 __in SIZE_T cchSource,
60 __in UINT uiCodepage
61 );
62HRESULT DAPI StrAllocStringAnsi(
63 __deref_out_ecount_z(cchSource+1) LPWSTR* ppwz,
64 __in_z LPCSTR szSource,
65 __in SIZE_T cchSource,
66 __in UINT uiCodepage
67 );
68HRESULT DAPI StrAnsiAllocStringAnsi(
69 __deref_out_ecount_z(cchSource+1) LPSTR* ppsz,
70 __in_z LPCSTR szSource,
71 __in SIZE_T cchSource
72 );
73HRESULT DAPI StrAllocPrefix(
74 __deref_out_z LPWSTR* ppwz,
75 __in_z LPCWSTR wzPrefix,
76 __in SIZE_T cchPrefix
77 );
78HRESULT DAPI StrAllocConcat(
79 __deref_out_z LPWSTR* ppwz,
80 __in_z LPCWSTR wzSource,
81 __in SIZE_T cchSource
82 );
83HRESULT DAPI StrAllocConcatSecure(
84 __deref_out_z LPWSTR* ppwz,
85 __in_z LPCWSTR wzSource,
86 __in SIZE_T cchSource
87 );
88HRESULT DAPI StrAnsiAllocConcat(
89 __deref_out_z LPSTR* ppz,
90 __in_z LPCSTR pzSource,
91 __in SIZE_T cchSource
92 );
93HRESULT __cdecl StrAllocFormatted(
94 __deref_out_z LPWSTR* ppwz,
95 __in __format_string LPCWSTR wzFormat,
96 ...
97 );
98HRESULT __cdecl StrAllocConcatFormatted(
99 __deref_out_z LPWSTR* ppwz,
100 __in __format_string LPCWSTR wzFormat,
101 ...
102 );
103HRESULT __cdecl StrAllocConcatFormattedSecure(
104 __deref_out_z LPWSTR* ppwz,
105 __in __format_string LPCWSTR wzFormat,
106 ...
107 );
108HRESULT __cdecl StrAllocFormattedSecure(
109 __deref_out_z LPWSTR* ppwz,
110 __in __format_string LPCWSTR wzFormat,
111 ...
112 );
113HRESULT __cdecl StrAnsiAllocFormatted(
114 __deref_out_z LPSTR* ppsz,
115 __in __format_string LPCSTR szFormat,
116 ...
117 );
118HRESULT DAPI StrAllocFormattedArgs(
119 __deref_out_z LPWSTR* ppwz,
120 __in __format_string LPCWSTR wzFormat,
121 __in va_list args
122 );
123HRESULT DAPI StrAllocFormattedArgsSecure(
124 __deref_out_z LPWSTR* ppwz,
125 __in __format_string LPCWSTR wzFormat,
126 __in va_list args
127 );
128HRESULT DAPI StrAnsiAllocFormattedArgs(
129 __deref_out_z LPSTR* ppsz,
130 __in __format_string LPCSTR szFormat,
131 __in va_list args
132 );
133HRESULT DAPI StrAllocFromError(
134 __inout LPWSTR *ppwzMessage,
135 __in HRESULT hrError,
136 __in_opt HMODULE hModule,
137 ...
138 );
139
140HRESULT DAPI StrMaxLength(
141 __in LPCVOID p,
142 __out SIZE_T* pcbch
143 );
144HRESULT DAPI StrSize(
145 __in LPCVOID p,
146 __out SIZE_T* pcbb
147 );
148
149HRESULT DAPI StrFree(
150 __in LPVOID p
151 );
152
153
154HRESULT DAPI StrReplaceStringAll(
155 __inout LPWSTR* ppwzOriginal,
156 __in_z LPCWSTR wzOldSubString,
157 __in_z LPCWSTR wzNewSubString
158 );
159HRESULT DAPI StrReplaceString(
160 __inout LPWSTR* ppwzOriginal,
161 __inout DWORD* pdwStartIndex,
162 __in_z LPCWSTR wzOldSubString,
163 __in_z LPCWSTR wzNewSubString
164 );
165
166HRESULT DAPI StrHexEncode(
167 __in_ecount(cbSource) const BYTE* pbSource,
168 __in SIZE_T cbSource,
169 __out_ecount(cchDest) LPWSTR wzDest,
170 __in SIZE_T cchDest
171 );
172HRESULT DAPI StrAllocHexEncode(
173 __in_ecount(cbSource) const BYTE* pbSource,
174 __in SIZE_T cbSource,
175 __deref_out_ecount_z(2*(cbSource+1)) LPWSTR* ppwzDest
176 );
177HRESULT DAPI StrHexDecode(
178 __in_z LPCWSTR wzSource,
179 __out_bcount(cbDest) BYTE* pbDest,
180 __in SIZE_T cbDest
181 );
182HRESULT DAPI StrAllocHexDecode(
183 __in_z LPCWSTR wzSource,
184 __out_bcount(*pcbDest) BYTE** ppbDest,
185 __out_opt DWORD* pcbDest
186 );
187
188HRESULT DAPI StrAllocBase85Encode(
189 __in_bcount_opt(cbSource) const BYTE* pbSource,
190 __in SIZE_T cbSource,
191 __deref_out_z LPWSTR* pwzDest
192 );
193HRESULT DAPI StrAllocBase85Decode(
194 __in_z LPCWSTR wzSource,
195 __deref_out_bcount(*pcbDest) BYTE** ppbDest,
196 __out SIZE_T* pcbDest
197);
198
199HRESULT DAPI MultiSzLen(
200 __in_ecount(*pcch) __nullnullterminated LPCWSTR pwzMultiSz,
201 __out SIZE_T* pcch
202 );
203HRESULT DAPI MultiSzPrepend(
204 __deref_inout_ecount(*pcchMultiSz) __nullnullterminated LPWSTR* ppwzMultiSz,
205 __inout_opt SIZE_T* pcchMultiSz,
206 __in __nullnullterminated LPCWSTR pwzInsert
207 );
208HRESULT DAPI MultiSzFindSubstring(
209 __in __nullnullterminated LPCWSTR pwzMultiSz,
210 __in __nullnullterminated LPCWSTR pwzSubstring,
211 __out_opt DWORD_PTR* pdwIndex,
212 __deref_opt_out __nullnullterminated LPCWSTR* ppwzFoundIn
213 );
214HRESULT DAPI MultiSzFindString(
215 __in __nullnullterminated LPCWSTR pwzMultiSz,
216 __in __nullnullterminated LPCWSTR pwzString,
217 __out_opt DWORD_PTR* pdwIndex,
218 __deref_opt_out __nullnullterminated LPCWSTR* ppwzFound
219 );
220HRESULT DAPI MultiSzRemoveString(
221 __deref_inout __nullnullterminated LPWSTR* ppwzMultiSz,
222 __in DWORD_PTR dwIndex
223 );
224HRESULT DAPI MultiSzInsertString(
225 __deref_inout __nullnullterminated LPWSTR* ppwzMultiSz,
226 __inout_opt SIZE_T* pcchMultiSz,
227 __in DWORD_PTR dwIndex,
228 __in_z LPCWSTR pwzInsert
229 );
230HRESULT DAPI MultiSzReplaceString(
231 __deref_inout __nullnullterminated LPWSTR* ppwzMultiSz,
232 __in DWORD_PTR dwIndex,
233 __in_z LPCWSTR pwzString
234 );
235
236LPCWSTR DAPI wcsistr(
237 __in_z LPCWSTR wzString,
238 __in_z LPCWSTR wzCharSet
239 );
240
241HRESULT DAPI StrStringToInt16(
242 __in_z LPCWSTR wzIn,
243 __in DWORD cchIn,
244 __out SHORT* psOut
245 );
246HRESULT DAPI StrStringToUInt16(
247 __in_z LPCWSTR wzIn,
248 __in DWORD cchIn,
249 __out USHORT* pusOut
250 );
251HRESULT DAPI StrStringToInt32(
252 __in_z LPCWSTR wzIn,
253 __in DWORD cchIn,
254 __out INT* piOut
255 );
256HRESULT DAPI StrStringToUInt32(
257 __in_z LPCWSTR wzIn,
258 __in DWORD cchIn,
259 __out UINT* puiOut
260 );
261HRESULT DAPI StrStringToInt64(
262 __in_z LPCWSTR wzIn,
263 __in DWORD cchIn,
264 __out LONGLONG* pllOut
265 );
266HRESULT DAPI StrStringToUInt64(
267 __in_z LPCWSTR wzIn,
268 __in DWORD cchIn,
269 __out ULONGLONG* pullOut
270 );
271void DAPI StrStringToUpper(
272 __inout_z LPWSTR wzIn
273 );
274void DAPI StrStringToLower(
275 __inout_z LPWSTR wzIn
276 );
277HRESULT DAPI StrAllocStringToUpperInvariant(
278 __deref_out_z LPWSTR* pscz,
279 __in_z LPCWSTR wzSource,
280 __in SIZE_T cchSource
281 );
282HRESULT DAPI StrAllocStringToLowerInvariant(
283 __deref_out_z LPWSTR* pscz,
284 __in_z LPCWSTR wzSource,
285 __in SIZE_T cchSource
286 );
287
288HRESULT DAPI StrArrayAllocString(
289 __deref_inout_ecount_opt(*pcStrArray) LPWSTR **prgsczStrArray,
290 __inout LPUINT pcStrArray,
291 __in_z LPCWSTR wzSource,
292 __in SIZE_T cchSource
293 );
294
295HRESULT DAPI StrArrayFree(
296 __in_ecount(cStrArray) LPWSTR *rgsczStrArray,
297 __in UINT cStrArray
298 );
299
300HRESULT DAPI StrSplitAllocArray(
301 __deref_inout_ecount_opt(*pcStrArray) LPWSTR **prgsczStrArray,
302 __inout LPUINT pcStrArray,
303 __in_z LPCWSTR wzSource,
304 __in_z LPCWSTR wzDelim
305 );
306
307HRESULT DAPI StrSecureZeroString(
308 __in LPWSTR pwz
309 );
310HRESULT DAPI StrSecureZeroFreeString(
311 __in LPWSTR pwz
312 );
313
314#ifdef __cplusplus
315}
316#endif
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/svcutil.h b/src/libs/dutil/WixToolset.DUtil/inc/svcutil.h
new file mode 100644
index 00000000..80d6326c
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/svcutil.h
@@ -0,0 +1,21 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9
10#define ReleaseServiceHandle(h) if (h) { ::CloseServiceHandle(h); h = NULL; }
11
12
13HRESULT DAPI SvcQueryConfig(
14 __in SC_HANDLE sch,
15 __out QUERY_SERVICE_CONFIGW** ppConfig
16 );
17
18
19#ifdef __cplusplus
20}
21#endif
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/thmutil.h b/src/libs/dutil/WixToolset.DUtil/inc/thmutil.h
new file mode 100644
index 00000000..d3dd6d21
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/thmutil.h
@@ -0,0 +1,765 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9#define ReleaseTheme(p) if (p) { ThemeFree(p); p = NULL; }
10
11typedef HRESULT(CALLBACK *PFNTHM_EVALUATE_VARIABLE_CONDITION)(
12 __in_z LPCWSTR wzCondition,
13 __out BOOL* pf,
14 __in_opt LPVOID pvContext
15 );
16typedef HRESULT(CALLBACK *PFNTHM_FORMAT_VARIABLE_STRING)(
17 __in_z LPCWSTR wzFormat,
18 __inout LPWSTR* psczOut,
19 __in_opt LPVOID pvContext
20 );
21typedef HRESULT(CALLBACK *PFNTHM_GET_VARIABLE_NUMERIC)(
22 __in_z LPCWSTR wzVariable,
23 __out LONGLONG* pllValue,
24 __in_opt LPVOID pvContext
25 );
26typedef HRESULT(CALLBACK *PFNTHM_SET_VARIABLE_NUMERIC)(
27 __in_z LPCWSTR wzVariable,
28 __in LONGLONG llValue,
29 __in_opt LPVOID pvContext
30 );
31typedef HRESULT(CALLBACK *PFNTHM_GET_VARIABLE_STRING)(
32 __in_z LPCWSTR wzVariable,
33 __inout LPWSTR* psczValue,
34 __in_opt LPVOID pvContext
35 );
36typedef HRESULT(CALLBACK *PFNTHM_SET_VARIABLE_STRING)(
37 __in_z LPCWSTR wzVariable,
38 __in_z_opt LPCWSTR wzValue,
39 __in BOOL fFormatted,
40 __in_opt LPVOID pvContext
41 );
42
43typedef enum THEME_ACTION_TYPE
44{
45 THEME_ACTION_TYPE_BROWSE_DIRECTORY,
46 THEME_ACTION_TYPE_CHANGE_PAGE,
47 THEME_ACTION_TYPE_CLOSE_WINDOW,
48} THEME_ACTION_TYPE;
49
50typedef enum THEME_CONTROL_DATA
51{
52 THEME_CONTROL_DATA_HOVER = 1,
53} THEME_CONTROL_DATA;
54
55typedef enum THEME_CONTROL_TYPE
56{
57 THEME_CONTROL_TYPE_UNKNOWN,
58 THEME_CONTROL_TYPE_BILLBOARD,
59 THEME_CONTROL_TYPE_BUTTON,
60 THEME_CONTROL_TYPE_CHECKBOX,
61 THEME_CONTROL_TYPE_COMBOBOX,
62 THEME_CONTROL_TYPE_COMMANDLINK,
63 THEME_CONTROL_TYPE_EDITBOX,
64 THEME_CONTROL_TYPE_HYPERLINK,
65 THEME_CONTROL_TYPE_HYPERTEXT,
66 THEME_CONTROL_TYPE_IMAGE,
67 THEME_CONTROL_TYPE_LABEL,
68 THEME_CONTROL_TYPE_PANEL,
69 THEME_CONTROL_TYPE_PROGRESSBAR,
70 THEME_CONTROL_TYPE_RADIOBUTTON,
71 THEME_CONTROL_TYPE_RICHEDIT,
72 THEME_CONTROL_TYPE_STATIC,
73 THEME_CONTROL_TYPE_LISTVIEW,
74 THEME_CONTROL_TYPE_TREEVIEW,
75 THEME_CONTROL_TYPE_TAB,
76} THEME_CONTROL_TYPE;
77
78typedef enum THEME_SHOW_PAGE_REASON
79{
80 THEME_SHOW_PAGE_REASON_DEFAULT,
81 THEME_SHOW_PAGE_REASON_CANCEL,
82 THEME_SHOW_PAGE_REASON_REFRESH,
83} THEME_SHOW_PAGE_REASON;
84
85typedef enum THEME_WINDOW_INITIAL_POSITION
86{
87 THEME_WINDOW_INITIAL_POSITION_DEFAULT,
88 THEME_WINDOW_INITIAL_POSITION_CENTER_MONITOR_FROM_COORDINATES,
89} THEME_WINDOW_INITIAL_POSITION;
90
91
92struct THEME_COLUMN
93{
94 LPWSTR pszName;
95 UINT uStringId;
96 int nDefaultDpiBaseWidth;
97 int nBaseWidth;
98 int nWidth;
99 BOOL fExpands;
100};
101
102
103struct THEME_TAB
104{
105 LPWSTR pszName;
106 UINT uStringId;
107};
108
109struct THEME_ACTION
110{
111 LPWSTR sczCondition;
112 THEME_ACTION_TYPE type;
113 union
114 {
115 struct
116 {
117 LPWSTR sczVariableName;
118 } BrowseDirectory;
119 struct
120 {
121 LPWSTR sczPageName;
122 BOOL fCancel;
123 } ChangePage;
124 };
125};
126
127struct THEME_CONDITIONAL_TEXT
128{
129 LPWSTR sczCondition;
130 LPWSTR sczText;
131};
132
133// THEME_ASSIGN_CONTROL_ID - Used to apply a specific id to a named control (usually
134// to set the WM_COMMAND).
135struct THEME_ASSIGN_CONTROL_ID
136{
137 WORD wId; // id to apply to control
138 LPCWSTR wzName; // name of control to match
139};
140
141const DWORD THEME_FIRST_ASSIGN_CONTROL_ID = 1024; // Recommended first control id to be assigned.
142
143struct THEME_CONTROL
144{
145 THEME_CONTROL_TYPE type;
146
147 WORD wId;
148 WORD wPageId;
149
150 LPWSTR sczName; // optional name for control, used to apply control id and link the control to a variable.
151 LPWSTR sczText;
152 LPWSTR sczTooltip;
153 LPWSTR sczNote; // optional text for command link
154 int nDefaultDpiX;
155 int nDefaultDpiY;
156 int nDefaultDpiHeight;
157 int nDefaultDpiWidth;
158 int nX;
159 int nY;
160 int nHeight;
161 int nWidth;
162 int nSourceX;
163 int nSourceY;
164 UINT uStringId;
165
166 LPWSTR sczEnableCondition;
167 LPWSTR sczVisibleCondition;
168 BOOL fDisableVariableFunctionality;
169
170 HBITMAP hImage;
171 HICON hIcon;
172
173 // Don't free these; it's just a handle to the central image lists stored in THEME. The handle is freed once, there.
174 HIMAGELIST rghImageList[4];
175
176 DWORD dwStyle;
177 DWORD dwExtendedStyle;
178 DWORD dwInternalStyle;
179
180 DWORD dwFontId;
181
182 // child controls
183 DWORD cControls;
184 THEME_CONTROL* rgControls;
185
186 // Used by billboard controls
187 WORD wBillboardInterval;
188 BOOL fBillboardLoops;
189
190 // Used by button and command link controls
191 THEME_ACTION* rgActions;
192 DWORD cActions;
193 THEME_ACTION* pDefaultAction;
194
195 // Used by hyperlink and owner-drawn button controls
196 DWORD dwFontHoverId;
197 DWORD dwFontSelectedId;
198
199 // Used by listview controls
200 THEME_COLUMN *ptcColumns;
201 DWORD cColumns;
202
203 // Used by radio button controls
204 BOOL fLastRadioButton;
205 LPWSTR sczValue;
206 LPWSTR sczVariable;
207
208 // Used by tab controls
209 THEME_TAB *pttTabs;
210 DWORD cTabs;
211
212 // Used by controls that have text
213 DWORD cConditionalText;
214 THEME_CONDITIONAL_TEXT* rgConditionalText;
215
216 // Used by command link controls
217 DWORD cConditionalNotes;
218 THEME_CONDITIONAL_TEXT* rgConditionalNotes;
219
220 // state variables that should be ignored
221 HWND hWnd;
222 DWORD dwData; // type specific data
223};
224
225
226struct THEME_IMAGELIST
227{
228 LPWSTR sczName;
229
230 HIMAGELIST hImageList;
231};
232
233struct THEME_SAVEDVARIABLE
234{
235 LPWSTR wzName;
236 LPWSTR sczValue;
237};
238
239struct THEME_PAGE
240{
241 WORD wId;
242 LPWSTR sczName;
243
244 DWORD cControlIndices;
245
246 DWORD cSavedVariables;
247 THEME_SAVEDVARIABLE* rgSavedVariables;
248};
249
250struct THEME_FONT_INSTANCE
251{
252 UINT nDpi;
253 HFONT hFont;
254};
255
256struct THEME_FONT
257{
258 LONG lfHeight;
259 LONG lfWeight;
260 BYTE lfUnderline;
261 BYTE lfQuality;
262 LPWSTR sczFaceName;
263
264 COLORREF crForeground;
265 HBRUSH hForeground;
266 COLORREF crBackground;
267 HBRUSH hBackground;
268
269 DWORD cFontInstances;
270 THEME_FONT_INSTANCE* rgFontInstances;
271};
272
273
274struct THEME
275{
276 WORD wId;
277
278 BOOL fAutoResize;
279 BOOL fForceResize;
280
281 DWORD dwStyle;
282 DWORD dwFontId;
283 HANDLE hIcon;
284 LPWSTR sczCaption;
285 int nDefaultDpiHeight;
286 int nDefaultDpiMinimumHeight;
287 int nDefaultDpiWidth;
288 int nDefaultDpiMinimumWidth;
289 int nHeight;
290 int nMinimumHeight;
291 int nWidth;
292 int nMinimumWidth;
293 int nWindowHeight;
294 int nWindowWidth;
295 int nSourceX;
296 int nSourceY;
297 UINT uStringId;
298
299 HBITMAP hImage;
300
301 DWORD cFonts;
302 THEME_FONT* rgFonts;
303
304 DWORD cPages;
305 THEME_PAGE* rgPages;
306
307 DWORD cImageLists;
308 THEME_IMAGELIST* rgImageLists;
309
310 DWORD cControls;
311 THEME_CONTROL* rgControls;
312
313 // internal state variables -- do not use outside ThmUtil.cpp
314 HWND hwndParent; // parent for loaded controls
315 HWND hwndHover; // current hwnd hovered over
316 DWORD dwCurrentPageId;
317 HWND hwndTooltip;
318
319 UINT nDpi;
320
321 // callback functions
322 PFNTHM_EVALUATE_VARIABLE_CONDITION pfnEvaluateCondition;
323 PFNTHM_FORMAT_VARIABLE_STRING pfnFormatString;
324 PFNTHM_GET_VARIABLE_NUMERIC pfnGetNumericVariable;
325 PFNTHM_SET_VARIABLE_NUMERIC pfnSetNumericVariable;
326 PFNTHM_GET_VARIABLE_STRING pfnGetStringVariable;
327 PFNTHM_SET_VARIABLE_STRING pfnSetStringVariable;
328
329 LPVOID pvVariableContext;
330};
331
332
333/********************************************************************
334 ThemeInitialize - initialized theme management.
335
336*******************************************************************/
337HRESULT DAPI ThemeInitialize(
338 __in_opt HMODULE hModule
339 );
340
341/********************************************************************
342 ThemeUninitialize - uninitialize theme management.
343
344*******************************************************************/
345void DAPI ThemeUninitialize();
346
347/********************************************************************
348 ThemeLoadFromFile - loads a theme from a loose file.
349
350 *******************************************************************/
351HRESULT DAPI ThemeLoadFromFile(
352 __in_z LPCWSTR wzThemeFile,
353 __out THEME** ppTheme
354 );
355
356/********************************************************************
357 ThemeLoadFromResource - loads a theme from a module's data resource.
358
359 NOTE: The resource data must be UTF-8 encoded.
360*******************************************************************/
361HRESULT DAPI ThemeLoadFromResource(
362 __in_opt HMODULE hModule,
363 __in_z LPCSTR szResource,
364 __out THEME** ppTheme
365 );
366
367/********************************************************************
368 ThemeFree - frees any memory associated with a theme.
369
370*******************************************************************/
371void DAPI ThemeFree(
372 __in THEME* pTheme
373 );
374
375/********************************************************************
376ThemeRegisterVariableCallbacks - registers a context and callbacks
377 for working with variables.
378
379*******************************************************************/
380HRESULT DAPI ThemeRegisterVariableCallbacks(
381 __in THEME* pTheme,
382 __in_opt PFNTHM_EVALUATE_VARIABLE_CONDITION pfnEvaluateCondition,
383 __in_opt PFNTHM_FORMAT_VARIABLE_STRING pfnFormatString,
384 __in_opt PFNTHM_GET_VARIABLE_NUMERIC pfnGetNumericVariable,
385 __in_opt PFNTHM_SET_VARIABLE_NUMERIC pfnSetNumericVariable,
386 __in_opt PFNTHM_GET_VARIABLE_STRING pfnGetStringVariable,
387 __in_opt PFNTHM_SET_VARIABLE_STRING pfnSetStringVariable,
388 __in_opt LPVOID pvContext
389 );
390
391/********************************************************************
392 ThemeCreateParentWindow - creates a parent window for the theme.
393
394*******************************************************************/
395HRESULT DAPI ThemeCreateParentWindow(
396 __in THEME* pTheme,
397 __in DWORD dwExStyle,
398 __in LPCWSTR szClassName,
399 __in LPCWSTR szWindowName,
400 __in DWORD dwStyle,
401 __in int x,
402 __in int y,
403 __in_opt HWND hwndParent,
404 __in_opt HINSTANCE hInstance,
405 __in_opt LPVOID lpParam,
406 __in THEME_WINDOW_INITIAL_POSITION initialPosition,
407 __out_opt HWND* phWnd
408 );
409
410/********************************************************************
411 ThemeLoadControls - creates the windows for all the theme controls
412 using the window created in ThemeCreateParentWindow.
413
414*******************************************************************/
415HRESULT DAPI ThemeLoadControls(
416 __in THEME* pTheme,
417 __in_ecount_opt(cAssignControlIds) const THEME_ASSIGN_CONTROL_ID* rgAssignControlIds,
418 __in DWORD cAssignControlIds
419 );
420
421/********************************************************************
422 ThemeUnloadControls - resets all the theme control windows so the theme
423 controls can be reloaded.
424
425*******************************************************************/
426void DAPI ThemeUnloadControls(
427 __in THEME* pTheme
428 );
429
430/********************************************************************
431 ThemeLocalize - Localizes all of the strings in the theme.
432
433*******************************************************************/
434HRESULT DAPI ThemeLocalize(
435 __in THEME *pTheme,
436 __in const WIX_LOCALIZATION *pLocStringSet
437 );
438
439HRESULT DAPI ThemeLoadStrings(
440 __in THEME* pTheme,
441 __in HMODULE hResModule
442 );
443
444/********************************************************************
445 ThemeLoadRichEditFromFile - Attach a richedit control to a RTF file.
446
447 *******************************************************************/
448HRESULT DAPI ThemeLoadRichEditFromFile(
449 __in THEME* pTheme,
450 __in DWORD dwControl,
451 __in_z LPCWSTR wzFileName,
452 __in HMODULE hModule
453 );
454
455/********************************************************************
456 ThemeLoadRichEditFromResource - Attach a richedit control to resource data.
457
458 *******************************************************************/
459HRESULT DAPI ThemeLoadRichEditFromResource(
460 __in THEME* pTheme,
461 __in DWORD dwControl,
462 __in_z LPCSTR szResourceName,
463 __in HMODULE hModule
464 );
465
466/********************************************************************
467 ThemeLoadRichEditFromResourceToHWnd - Attach a richedit control (by
468 HWND) to resource data.
469
470 *******************************************************************/
471HRESULT DAPI ThemeLoadRichEditFromResourceToHWnd(
472 __in HWND hWnd,
473 __in_z LPCSTR szResourceName,
474 __in HMODULE hModule
475 );
476
477/********************************************************************
478 ThemeHandleKeyboardMessage - will translate the message using the active
479 accelerator table.
480
481*******************************************************************/
482BOOL DAPI ThemeHandleKeyboardMessage(
483 __in_opt THEME* pTheme,
484 __in HWND hWnd,
485 __in MSG* pMsg
486 );
487
488/********************************************************************
489 ThemeDefWindowProc - replacement for DefWindowProc() when using theme.
490
491*******************************************************************/
492LRESULT CALLBACK ThemeDefWindowProc(
493 __in_opt THEME* pTheme,
494 __in HWND hWnd,
495 __in UINT uMsg,
496 __in WPARAM wParam,
497 __in LPARAM lParam
498 );
499
500/********************************************************************
501 ThemeGetPageIds - gets the page ids for the theme via page names.
502
503*******************************************************************/
504void DAPI ThemeGetPageIds(
505 __in const THEME* pTheme,
506 __in_ecount(cGetPages) LPCWSTR* rgwzFindNames,
507 __inout_ecount(cGetPages) DWORD* rgdwPageIds,
508 __in DWORD cGetPages
509 );
510
511/********************************************************************
512 ThemeGetPage - gets a theme page by id.
513
514 *******************************************************************/
515THEME_PAGE* DAPI ThemeGetPage(
516 __in const THEME* pTheme,
517 __in DWORD dwPage
518 );
519
520/********************************************************************
521 ThemeShowPage - shows or hides all of the controls in the page at one time.
522
523 *******************************************************************/
524HRESULT DAPI ThemeShowPage(
525 __in THEME* pTheme,
526 __in DWORD dwPage,
527 __in int nCmdShow
528 );
529
530/********************************************************************
531ThemeShowPageEx - shows or hides all of the controls in the page at one time.
532 When using variables, TSPR_CANCEL reverts any changes made.
533 TSPR_REFRESH forces reevaluation of conditions.
534 It is expected that the current page is hidden before
535 showing a new page.
536
537*******************************************************************/
538HRESULT DAPI ThemeShowPageEx(
539 __in THEME* pTheme,
540 __in DWORD dwPage,
541 __in int nCmdShow,
542 __in THEME_SHOW_PAGE_REASON reason
543 );
544
545
546/********************************************************************
547ThemeShowChild - shows a control's specified child control, hiding the rest.
548
549*******************************************************************/
550void DAPI ThemeShowChild(
551 __in THEME* pTheme,
552 __in THEME_CONTROL* pParentControl,
553 __in DWORD dwIndex
554 );
555
556/********************************************************************
557 ThemeControlExists - check if a control with the specified id exists.
558
559 *******************************************************************/
560BOOL DAPI ThemeControlExists(
561 __in const THEME* pTheme,
562 __in DWORD dwControl
563 );
564
565/********************************************************************
566 ThemeControlEnable - enables/disables a control.
567
568 *******************************************************************/
569void DAPI ThemeControlEnable(
570 __in THEME* pTheme,
571 __in DWORD dwControl,
572 __in BOOL fEnable
573 );
574
575/********************************************************************
576 ThemeControlEnabled - returns whether a control is enabled/disabled.
577
578 *******************************************************************/
579BOOL DAPI ThemeControlEnabled(
580 __in THEME* pTheme,
581 __in DWORD dwControl
582 );
583
584/********************************************************************
585 ThemeControlElevates - sets/removes the shield icon on a control.
586
587 *******************************************************************/
588void DAPI ThemeControlElevates(
589 __in THEME* pTheme,
590 __in DWORD dwControl,
591 __in BOOL fElevates
592 );
593
594/********************************************************************
595 ThemeShowControl - shows/hides a control.
596
597 *******************************************************************/
598void DAPI ThemeShowControl(
599 __in THEME* pTheme,
600 __in DWORD dwControl,
601 __in int nCmdShow
602 );
603
604/********************************************************************
605ThemeShowControlEx - shows/hides a control with support for
606conditional text and notes.
607
608*******************************************************************/
609void DAPI ThemeShowControlEx(
610 __in THEME* pTheme,
611 __in DWORD dwControl,
612 __in int nCmdShow
613 );
614
615/********************************************************************
616 ThemeControlVisible - returns whether a control is visible.
617
618 *******************************************************************/
619BOOL DAPI ThemeControlVisible(
620 __in THEME* pTheme,
621 __in DWORD dwControl
622 );
623
624BOOL DAPI ThemePostControlMessage(
625 __in THEME* pTheme,
626 __in DWORD dwControl,
627 __in UINT Msg,
628 __in WPARAM wParam,
629 __in LPARAM lParam
630 );
631
632LRESULT DAPI ThemeSendControlMessage(
633 __in const THEME* pTheme,
634 __in DWORD dwControl,
635 __in UINT Msg,
636 __in WPARAM wParam,
637 __in LPARAM lParam
638 );
639
640/********************************************************************
641 ThemeDrawBackground - draws the theme background.
642
643*******************************************************************/
644HRESULT DAPI ThemeDrawBackground(
645 __in THEME* pTheme,
646 __in PAINTSTRUCT* pps
647 );
648
649/********************************************************************
650 ThemeDrawControl - draw an owner drawn control.
651
652*******************************************************************/
653HRESULT DAPI ThemeDrawControl(
654 __in THEME* pTheme,
655 __in DRAWITEMSTRUCT* pdis
656 );
657
658/********************************************************************
659 ThemeHoverControl - mark a control as hover.
660
661*******************************************************************/
662BOOL DAPI ThemeHoverControl(
663 __in THEME* pTheme,
664 __in HWND hwndParent,
665 __in HWND hwndControl
666 );
667
668/********************************************************************
669 ThemeIsControlChecked - gets whether a control is checked. Only
670 really useful for checkbox controls.
671
672*******************************************************************/
673BOOL DAPI ThemeIsControlChecked(
674 __in THEME* pTheme,
675 __in DWORD dwControl
676 );
677
678/********************************************************************
679 ThemeSetControlColor - sets the color of text for a control.
680
681*******************************************************************/
682BOOL DAPI ThemeSetControlColor(
683 __in THEME* pTheme,
684 __in HDC hdc,
685 __in HWND hWnd,
686 __out HBRUSH* phBackgroundBrush
687 );
688
689/********************************************************************
690 ThemeSetProgressControl - sets the current percentage complete in a
691 progress bar control.
692
693*******************************************************************/
694HRESULT DAPI ThemeSetProgressControl(
695 __in THEME* pTheme,
696 __in DWORD dwControl,
697 __in DWORD dwProgressPercentage
698 );
699
700/********************************************************************
701 ThemeSetProgressControlColor - sets the current color of a
702 progress bar control.
703
704*******************************************************************/
705HRESULT DAPI ThemeSetProgressControlColor(
706 __in THEME* pTheme,
707 __in DWORD dwControl,
708 __in DWORD dwColorIndex
709 );
710
711/********************************************************************
712 ThemeSetTextControl - sets the text of a control.
713
714*******************************************************************/
715HRESULT DAPI ThemeSetTextControl(
716 __in const THEME* pTheme,
717 __in DWORD dwControl,
718 __in_z_opt LPCWSTR wzText
719 );
720
721/********************************************************************
722ThemeSetTextControl - sets the text of a control and optionally
723 invalidates the control.
724
725*******************************************************************/
726HRESULT DAPI ThemeSetTextControlEx(
727 __in const THEME* pTheme,
728 __in DWORD dwControl,
729 __in BOOL fUpdate,
730 __in_z_opt LPCWSTR wzText
731 );
732
733/********************************************************************
734 ThemeGetTextControl - gets the text of a control.
735
736*******************************************************************/
737HRESULT DAPI ThemeGetTextControl(
738 __in const THEME* pTheme,
739 __in DWORD dwControl,
740 __inout_z LPWSTR* psczText
741 );
742
743/********************************************************************
744 ThemeUpdateCaption - updates the caption in the theme.
745
746*******************************************************************/
747HRESULT DAPI ThemeUpdateCaption(
748 __in THEME* pTheme,
749 __in_z LPCWSTR wzCaption
750 );
751
752/********************************************************************
753 ThemeSetFocus - set the focus to the control supplied or the next
754 enabled control if it is disabled.
755
756*******************************************************************/
757void DAPI ThemeSetFocus(
758 __in THEME* pTheme,
759 __in DWORD dwControl
760 );
761
762#ifdef __cplusplus
763}
764#endif
765
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/timeutil.h b/src/libs/dutil/WixToolset.DUtil/inc/timeutil.h
new file mode 100644
index 00000000..3655c00a
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/timeutil.h
@@ -0,0 +1,38 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9HRESULT DAPI TimeFromString(
10 __in_z LPCWSTR wzTime,
11 __out FILETIME* pFileTime
12 );
13HRESULT DAPI TimeFromString3339(
14 __in_z LPCWSTR wzTime,
15 __out FILETIME* pFileTime
16 );
17HRESULT DAPI TimeCurrentTime(
18 __deref_out_z LPWSTR* ppwz,
19 __in BOOL fGMT
20 );
21HRESULT DAPI TimeCurrentDateTime(
22 __deref_out_z LPWSTR* ppwz,
23 __in BOOL fGMT
24 );
25HRESULT DAPI TimeSystemDateTime(
26 __deref_out_z LPWSTR* ppwz,
27 __in const SYSTEMTIME *pst,
28 __in BOOL fGMT
29 );
30HRESULT DAPI TimeSystemToDateTimeString(
31 __deref_out_z LPWSTR* ppwz,
32 __in const SYSTEMTIME *pst,
33 __in LCID locale
34 );
35
36#ifdef __cplusplus
37}
38#endif
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/uncutil.h b/src/libs/dutil/WixToolset.DUtil/inc/uncutil.h
new file mode 100644
index 00000000..6516a801
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/uncutil.h
@@ -0,0 +1,20 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9/*******************************************************************
10 UncConvertFromMountedDrive - Converts the string in-place from a
11 mounted drive path to a UNC path
12*******************************************************************/
13DAPI_(HRESULT) UncConvertFromMountedDrive(
14 __inout LPWSTR *psczUNCPath,
15 __in LPCWSTR sczMountedDrivePath
16 );
17
18#ifdef __cplusplus
19}
20#endif
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/uriutil.h b/src/libs/dutil/WixToolset.DUtil/inc/uriutil.h
new file mode 100644
index 00000000..d6dfdd6b
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/uriutil.h
@@ -0,0 +1,100 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#include "wininet.h"
6
7
8#ifdef __cplusplus
9extern "C" {
10#endif
11
12typedef enum URI_PROTOCOL
13{
14 URI_PROTOCOL_UNKNOWN,
15 URI_PROTOCOL_FILE,
16 URI_PROTOCOL_FTP,
17 URI_PROTOCOL_HTTP,
18 URI_PROTOCOL_HTTPS,
19 URI_PROTOCOL_LOCAL,
20 URI_PROTOCOL_UNC
21} URI_PROTOCOL;
22
23typedef struct _URI_INFO
24{
25 INTERNET_SCHEME scheme;
26 LPWSTR sczHostName;
27 INTERNET_PORT port;
28 LPWSTR sczUser;
29 LPWSTR sczPassword;
30 LPWSTR sczPath;
31 LPWSTR sczQueryString;
32} URI_INFO;
33
34
35HRESULT DAPI UriCanonicalize(
36 __inout_z LPWSTR* psczUri
37 );
38
39HRESULT DAPI UriCrack(
40 __in_z LPCWSTR wzUri,
41 __out_opt INTERNET_SCHEME* pScheme,
42 __deref_opt_out_z LPWSTR* psczHostName,
43 __out_opt INTERNET_PORT* pPort,
44 __deref_opt_out_z LPWSTR* psczUser,
45 __deref_opt_out_z LPWSTR* psczPassword,
46 __deref_opt_out_z LPWSTR* psczPath,
47 __deref_opt_out_z LPWSTR* psczQueryString
48 );
49
50HRESULT DAPI UriCrackEx(
51 __in_z LPCWSTR wzUri,
52 __in URI_INFO* pUriInfo
53 );
54
55void DAPI UriInfoUninitialize(
56 __in URI_INFO* pUriInfo
57 );
58
59HRESULT DAPI UriCreate(
60 __inout_z LPWSTR* psczUri,
61 __in INTERNET_SCHEME scheme,
62 __in_z_opt LPWSTR wzHostName,
63 __in INTERNET_PORT port,
64 __in_z_opt LPWSTR wzUser,
65 __in_z_opt LPWSTR wzPassword,
66 __in_z_opt LPWSTR wzPath,
67 __in_z_opt LPWSTR wzQueryString
68 );
69
70HRESULT DAPI UriCanonicalize(
71 __inout_z LPWSTR* psczUri
72 );
73
74HRESULT DAPI UriFile(
75 __deref_out_z LPWSTR* psczFile,
76 __in_z LPCWSTR wzUri
77 );
78
79HRESULT DAPI UriProtocol(
80 __in_z LPCWSTR wzUri,
81 __out URI_PROTOCOL* pProtocol
82 );
83
84HRESULT DAPI UriRoot(
85 __in_z LPCWSTR wzUri,
86 __out LPWSTR* ppwzRoot,
87 __out_opt URI_PROTOCOL* pProtocol
88 );
89
90HRESULT DAPI UriResolve(
91 __in_z LPCWSTR wzUri,
92 __in_opt LPCWSTR wzBaseUri,
93 __out LPWSTR* ppwzResolvedUri,
94 __out_opt URI_PROTOCOL* pResolvedProtocol
95 );
96
97#ifdef __cplusplus
98}
99#endif
100
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/userutil.h b/src/libs/dutil/WixToolset.DUtil/inc/userutil.h
new file mode 100644
index 00000000..2c86d229
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/userutil.h
@@ -0,0 +1,32 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9HRESULT DAPI UserBuildDomainUserName(
10 __out_ecount_z(cchDest) LPWSTR wzDest,
11 __in int cchDest,
12 __in_z LPCWSTR pwzName,
13 __in_z LPCWSTR pwzDomain
14 );
15
16HRESULT DAPI UserCheckIsMember(
17 __in_z LPCWSTR pwzName,
18 __in_z LPCWSTR pwzDomain,
19 __in_z LPCWSTR pwzGroupName,
20 __in_z LPCWSTR pwzGroupDomain,
21 __out LPBOOL lpfMember
22 );
23
24HRESULT DAPI UserCreateADsPath(
25 __in_z LPCWSTR wzObjectDomain,
26 __in_z LPCWSTR wzObjectName,
27 __out BSTR *pbstrAdsPath
28 );
29
30#ifdef __cplusplus
31}
32#endif
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/verutil.h b/src/libs/dutil/WixToolset.DUtil/inc/verutil.h
new file mode 100644
index 00000000..5247bb61
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/verutil.h
@@ -0,0 +1,93 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9#define ReleaseVerutilVersion(p) if (p) { VerFreeVersion(p); p = NULL; }
10
11typedef struct _VERUTIL_VERSION_RELEASE_LABEL
12{
13 BOOL fNumeric;
14 DWORD dwValue;
15 SIZE_T cchLabelOffset;
16 int cchLabel;
17} VERUTIL_VERSION_RELEASE_LABEL;
18
19typedef struct _VERUTIL_VERSION
20{
21 LPWSTR sczVersion;
22 DWORD dwMajor;
23 DWORD dwMinor;
24 DWORD dwPatch;
25 DWORD dwRevision;
26 DWORD cReleaseLabels;
27 VERUTIL_VERSION_RELEASE_LABEL* rgReleaseLabels;
28 SIZE_T cchMetadataOffset;
29 BOOL fInvalid;
30} VERUTIL_VERSION;
31
32/*******************************************************************
33 VerCompareParsedVersions - compares the Verutil versions.
34
35*******************************************************************/
36HRESULT DAPI VerCompareParsedVersions(
37 __in_opt VERUTIL_VERSION* pVersion1,
38 __in_opt VERUTIL_VERSION* pVersion2,
39 __out int* pnResult
40 );
41
42/*******************************************************************
43 VerCompareStringVersions - parses the strings with VerParseVersion and then
44 compares the Verutil versions with VerCompareParsedVersions.
45
46*******************************************************************/
47HRESULT DAPI VerCompareStringVersions(
48 __in_z LPCWSTR wzVersion1,
49 __in_z LPCWSTR wzVersion2,
50 __in BOOL fStrict,
51 __out int* pnResult
52 );
53
54/********************************************************************
55 VerCopyVersion - copies the given Verutil version.
56
57*******************************************************************/
58HRESULT DAPI VerCopyVersion(
59 __in VERUTIL_VERSION* pSource,
60 __out VERUTIL_VERSION** ppVersion
61 );
62
63/********************************************************************
64 VerFreeVersion - frees any memory associated with a Verutil version.
65
66*******************************************************************/
67void DAPI VerFreeVersion(
68 __in VERUTIL_VERSION* pVersion
69 );
70
71/*******************************************************************
72 VerParseVersion - parses the string into a Verutil version.
73
74*******************************************************************/
75HRESULT DAPI VerParseVersion(
76 __in_z LPCWSTR wzVersion,
77 __in SIZE_T cchVersion,
78 __in BOOL fStrict,
79 __out VERUTIL_VERSION** ppVersion
80 );
81
82/*******************************************************************
83 VerParseVersion - parses the QWORD into a Verutil version.
84
85*******************************************************************/
86HRESULT DAPI VerVersionFromQword(
87 __in DWORD64 qwVersion,
88 __out VERUTIL_VERSION** ppVersion
89 );
90
91#ifdef __cplusplus
92}
93#endif
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/wiutil.h b/src/libs/dutil/WixToolset.DUtil/inc/wiutil.h
new file mode 100644
index 00000000..9c2de209
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/wiutil.h
@@ -0,0 +1,402 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9// constants
10
11#define IDNOACTION 0
12#define WIU_MB_OKIGNORECANCELRETRY 0xE
13
14#define MAX_DARWIN_KEY 73
15#define MAX_DARWIN_COLUMN 255
16
17#define WIU_LOG_DEFAULT INSTALLLOGMODE_FATALEXIT | INSTALLLOGMODE_ERROR | INSTALLLOGMODE_WARNING | \
18 INSTALLLOGMODE_USER | INSTALLLOGMODE_INFO | INSTALLLOGMODE_RESOLVESOURCE | \
19 INSTALLLOGMODE_OUTOFDISKSPACE | INSTALLLOGMODE_ACTIONSTART | \
20 INSTALLLOGMODE_ACTIONDATA | INSTALLLOGMODE_COMMONDATA | INSTALLLOGMODE_PROPERTYDUMP
21
22#define ReleaseMsi(h) if (h) { ::MsiCloseHandle(h); }
23#define ReleaseNullMsi(h) if (h) { ::MsiCloseHandle(h); h = NULL; }
24
25
26typedef enum WIU_RESTART
27{
28 WIU_RESTART_NONE,
29 WIU_RESTART_REQUIRED,
30 WIU_RESTART_INITIATED,
31} WIU_RESTART;
32
33typedef enum WIU_MSI_EXECUTE_MESSAGE_TYPE
34{
35 WIU_MSI_EXECUTE_MESSAGE_NONE,
36 WIU_MSI_EXECUTE_MESSAGE_PROGRESS,
37 WIU_MSI_EXECUTE_MESSAGE_ERROR,
38 WIU_MSI_EXECUTE_MESSAGE_MSI_MESSAGE,
39 WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE,
40} WIU_MSI_EXECUTE_MESSAGE_TYPE;
41
42
43// structures
44
45typedef struct _WIU_MSI_EXECUTE_MESSAGE
46{
47 WIU_MSI_EXECUTE_MESSAGE_TYPE type;
48 DWORD dwAllowedResults;
49
50 DWORD cData;
51 LPCWSTR* rgwzData;
52
53 INT nResultRecommendation; // recommended return result for this message based on analysis of real world installs.
54
55 union
56 {
57 struct
58 {
59 DWORD dwPercentage;
60 } progress;
61 struct
62 {
63 DWORD dwErrorCode;
64 LPCWSTR wzMessage;
65 } error;
66 struct
67 {
68 INSTALLMESSAGE mt;
69 LPCWSTR wzMessage;
70 } msiMessage;
71 struct
72 {
73 DWORD cFiles;
74 LPCWSTR* rgwzFiles;
75 } msiFilesInUse;
76 };
77} WIU_MSI_EXECUTE_MESSAGE;
78
79typedef struct _WIU_MSI_PROGRESS
80{
81 DWORD dwTotal;
82 DWORD dwCompleted;
83 DWORD dwStep;
84 BOOL fMoveForward;
85 BOOL fEnableActionData;
86 BOOL fScriptInProgress;
87} WIU_MSI_PROGRESS;
88
89
90typedef int (*PFN_MSIEXECUTEMESSAGEHANDLER)(
91 __in WIU_MSI_EXECUTE_MESSAGE* pMessage,
92 __in_opt LPVOID pvContext
93 );
94
95typedef struct _WIU_MSI_EXECUTE_CONTEXT
96{
97 BOOL fRollback;
98 PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler;
99 LPVOID pvContext;
100 WIU_MSI_PROGRESS rgMsiProgress[64];
101 DWORD dwCurrentProgressIndex;
102
103 INSTALLUILEVEL previousInstallUILevel;
104 HWND hwndPreviousParentWindow;
105 INSTALLUI_HANDLERW pfnPreviousExternalUI;
106 INSTALLUI_HANDLER_RECORD pfnPreviousExternalUIRecord;
107
108 BOOL fSetPreviousExternalUIRecord;
109 BOOL fSetPreviousExternalUI;
110} WIU_MSI_EXECUTE_CONTEXT;
111
112
113// typedefs
114typedef UINT (WINAPI *PFN_MSIENABLELOGW)(
115 __in DWORD dwLogMode,
116 __in_z LPCWSTR szLogFile,
117 __in DWORD dwLogAttributes
118 );
119typedef UINT (WINAPI *PFN_MSIGETPRODUCTINFOW)(
120 __in LPCWSTR szProductCode,
121 __in LPCWSTR szProperty,
122 __out_ecount_opt(*pcchValue) LPWSTR szValue,
123 __inout LPDWORD pcchValue
124 );
125typedef INSTALLSTATE (WINAPI *PFN_MSIGETCOMPONENTPATHW)(
126 __in LPCWSTR szProduct,
127 __in LPCWSTR szComponent,
128 __out_ecount_opt(*pcchBuf) LPWSTR lpPathBuf,
129 __inout_opt LPDWORD pcchBuf
130 );
131typedef INSTALLSTATE (WINAPI *PFN_MSILOCATECOMPONENTW)(
132 __in LPCWSTR szComponent,
133 __out_ecount_opt(*pcchBuf) LPWSTR lpPathBuf,
134 __inout_opt LPDWORD pcchBuf
135 );
136typedef UINT (WINAPI *PFN_MSIGETPRODUCTINFOEXW)(
137 __in LPCWSTR szProductCode,
138 __in_opt LPCWSTR szUserSid,
139 __in MSIINSTALLCONTEXT dwContext,
140 __in LPCWSTR szProperty,
141 __out_ecount_opt(*pcchValue) LPWSTR szValue,
142 __inout_opt LPDWORD pcchValue
143 );
144typedef INSTALLSTATE (WINAPI *PFN_MSIQUERYFEATURESTATEW)(
145 __in LPCWSTR szProduct,
146 __in LPCWSTR szFeature
147 );
148typedef UINT (WINAPI *PFN_MSIGETPATCHINFOEXW)(
149 __in_z LPCWSTR wzPatchCode,
150 __in_z LPCWSTR wzProductCode,
151 __in_z_opt LPCWSTR wzUserSid,
152 __in MSIINSTALLCONTEXT dwContext,
153 __in_z LPCWSTR wzProperty,
154 __out_opt LPWSTR wzValue,
155 __inout DWORD* pcchValue
156 );
157typedef UINT (WINAPI *PFN_MSIDETERMINEPATCHSEQUENCEW)(
158 __in_z LPCWSTR wzProductCode,
159 __in_z_opt LPCWSTR wzUserSid,
160 __in MSIINSTALLCONTEXT context,
161 __in DWORD cPatchInfo,
162 __in PMSIPATCHSEQUENCEINFOW pPatchInfo
163 );
164typedef UINT (WINAPI *PFN_MSIDETERMINEAPPLICABLEPATCHESW)(
165 __in_z LPCWSTR wzProductPackagePath,
166 __in DWORD cPatchInfo,
167 __in PMSIPATCHSEQUENCEINFOW pPatchInfo
168 );
169typedef UINT (WINAPI *PFN_MSIINSTALLPRODUCTW)(
170 __in LPCWSTR szPackagePath,
171 __in_opt LPCWSTR szCommandLine
172 );
173typedef UINT (WINAPI *PFN_MSICONFIGUREPRODUCTEXW)(
174 __in LPCWSTR szProduct,
175 __in int iInstallLevel,
176 __in INSTALLSTATE eInstallState,
177 __in_opt LPCWSTR szCommandLine
178 );
179typedef UINT (WINAPI *PFN_MSIREMOVEPATCHESW)(
180 __in_z LPCWSTR wzPatchList,
181 __in_z LPCWSTR wzProductCode,
182 __in INSTALLTYPE eUninstallType,
183 __in_z_opt LPCWSTR szPropertyList
184 );
185typedef INSTALLUILEVEL (WINAPI *PFN_MSISETINTERNALUI)(
186 __in INSTALLUILEVEL dwUILevel,
187 __inout_opt HWND *phWnd
188 );
189typedef UINT (WINAPI *PFN_MSISETEXTERNALUIRECORD)(
190 __in_opt INSTALLUI_HANDLER_RECORD puiHandler,
191 __in DWORD dwMessageFilter,
192 __in_opt LPVOID pvContext,
193 __out_opt PINSTALLUI_HANDLER_RECORD ppuiPrevHandler
194 );
195typedef INSTALLUI_HANDLERW (WINAPI *PFN_MSISETEXTERNALUIW)(
196 __in_opt INSTALLUI_HANDLERW puiHandler,
197 __in DWORD dwMessageFilter,
198 __in_opt LPVOID pvContext
199 );
200typedef UINT (WINAPI *PFN_MSIENUMPRODUCTSW)(
201 __in DWORD iProductIndex,
202 __out_ecount(MAX_GUID_CHARS + 1) LPWSTR lpProductBuf
203 );
204typedef UINT (WINAPI *PFN_MSIENUMPRODUCTSEXW)(
205 __in_z_opt LPCWSTR wzProductCode,
206 __in_z_opt LPCWSTR wzUserSid,
207 __in DWORD dwContext,
208 __in DWORD dwIndex,
209 __out_opt WCHAR wzInstalledProductCode[39],
210 __out_opt MSIINSTALLCONTEXT *pdwInstalledContext,
211 __out_opt LPWSTR wzSid,
212 __inout_opt LPDWORD pcchSid
213 );
214
215typedef UINT (WINAPI *PFN_MSIENUMRELATEDPRODUCTSW)(
216 __in LPCWSTR lpUpgradeCode,
217 __reserved DWORD dwReserved,
218 __in DWORD iProductIndex,
219 __out_ecount(MAX_GUID_CHARS + 1) LPWSTR lpProductBuf
220 );
221typedef UINT (WINAPI *PFN_MSISOURCELISTADDSOURCEEXW)(
222 __in LPCWSTR szProductCodeOrPatchCode,
223 __in_opt LPCWSTR szUserSid,
224 __in MSIINSTALLCONTEXT dwContext,
225 __in DWORD dwOptions,
226 __in LPCWSTR szSource,
227 __in_opt DWORD dwIndex
228 );
229typedef UINT(WINAPI* PFN_MSIBEGINTRANSACTIONW)(
230 __in LPCWSTR szName,
231 __in DWORD dwTransactionAttributes,
232 __out MSIHANDLE* phTransactionHandle,
233 __out HANDLE* phChangeOfOwnerEvent
234 );
235typedef UINT(WINAPI* PFN_MSIENDTRANSACTION)(
236 __in DWORD dwTransactionState
237 );
238
239
240HRESULT DAPI WiuInitialize(
241 );
242void DAPI WiuUninitialize(
243 );
244void DAPI WiuFunctionOverride(
245 __in_opt PFN_MSIENABLELOGW pfnMsiEnableLogW,
246 __in_opt PFN_MSIGETCOMPONENTPATHW pfnMsiGetComponentPathW,
247 __in_opt PFN_MSILOCATECOMPONENTW pfnMsiLocateComponentW,
248 __in_opt PFN_MSIQUERYFEATURESTATEW pfnMsiQueryFeatureStateW,
249 __in_opt PFN_MSIGETPRODUCTINFOW pfnMsiGetProductInfoW,
250 __in_opt PFN_MSIGETPRODUCTINFOEXW pfnMsiGetProductInfoExW,
251 __in_opt PFN_MSIINSTALLPRODUCTW pfnMsiInstallProductW,
252 __in_opt PFN_MSICONFIGUREPRODUCTEXW pfnMsiConfigureProductExW,
253 __in_opt PFN_MSISETINTERNALUI pfnMsiSetInternalUI,
254 __in_opt PFN_MSISETEXTERNALUIW pfnMsiSetExternalUIW,
255 __in_opt PFN_MSIENUMRELATEDPRODUCTSW pfnMsiEnumRelatedProductsW,
256 __in_opt PFN_MSISETEXTERNALUIRECORD pfnMsiSetExternalUIRecord,
257 __in_opt PFN_MSISOURCELISTADDSOURCEEXW pfnMsiSourceListAddSourceExW
258 );
259HRESULT DAPI WiuGetComponentPath(
260 __in_z LPCWSTR wzProductCode,
261 __in_z LPCWSTR wzComponentId,
262 __out INSTALLSTATE* pInstallState,
263 __out_z LPWSTR* psczValue
264 );
265HRESULT DAPI WiuLocateComponent(
266 __in_z LPCWSTR wzComponentId,
267 __out INSTALLSTATE* pInstallState,
268 __out_z LPWSTR* psczValue
269 );
270HRESULT DAPI WiuQueryFeatureState(
271 __in_z LPCWSTR wzProduct,
272 __in_z LPCWSTR wzFeature,
273 __out INSTALLSTATE* pInstallState
274 );
275HRESULT DAPI WiuGetProductInfo(
276 __in_z LPCWSTR wzProductCode,
277 __in_z LPCWSTR wzProperty,
278 __out LPWSTR* psczValue
279 );
280HRESULT DAPI WiuGetProductInfoEx(
281 __in_z LPCWSTR wzProductCode,
282 __in_z_opt LPCWSTR wzUserSid,
283 __in MSIINSTALLCONTEXT dwContext,
284 __in_z LPCWSTR wzProperty,
285 __out LPWSTR* psczValue
286 );
287HRESULT DAPI WiuGetProductProperty(
288 __in MSIHANDLE hProduct,
289 __in_z LPCWSTR wzProperty,
290 __out LPWSTR* psczValue
291 );
292HRESULT DAPI WiuGetPatchInfoEx(
293 __in_z LPCWSTR wzPatchCode,
294 __in_z LPCWSTR wzProductCode,
295 __in_z_opt LPCWSTR wzUserSid,
296 __in MSIINSTALLCONTEXT dwContext,
297 __in_z LPCWSTR wzProperty,
298 __out LPWSTR* psczValue
299 );
300HRESULT DAPI WiuDeterminePatchSequence(
301 __in_z LPCWSTR wzProductCode,
302 __in_z_opt LPCWSTR wzUserSid,
303 __in MSIINSTALLCONTEXT context,
304 __in PMSIPATCHSEQUENCEINFOW pPatchInfo,
305 __in DWORD cPatchInfo
306 );
307HRESULT DAPI WiuDetermineApplicablePatches(
308 __in_z LPCWSTR wzProductPackagePath,
309 __in PMSIPATCHSEQUENCEINFOW pPatchInfo,
310 __in DWORD cPatchInfo
311 );
312HRESULT DAPI WiuEnumProducts(
313 __in DWORD iProductIndex,
314 __out_ecount(MAX_GUID_CHARS + 1) LPWSTR wzProductCode
315 );
316HRESULT DAPI WiuEnumProductsEx(
317 __in_z_opt LPCWSTR wzProductCode,
318 __in_z_opt LPCWSTR wzUserSid,
319 __in DWORD dwContext,
320 __in DWORD dwIndex,
321 __out_opt WCHAR wzInstalledProductCode[39],
322 __out_opt MSIINSTALLCONTEXT *pdwInstalledContext,
323 __out_opt LPWSTR wzSid,
324 __inout_opt LPDWORD pcchSid
325 );
326HRESULT DAPI WiuEnumRelatedProducts(
327 __in_z LPCWSTR wzUpgradeCode,
328 __in DWORD iProductIndex,
329 __out_ecount(MAX_GUID_CHARS + 1) LPWSTR wzProductCode
330 );
331HRESULT DAPI WiuEnumRelatedProductCodes(
332 __in_z LPCWSTR wzUpgradeCode,
333 __deref_out_ecount_opt(*pcRelatedProducts) LPWSTR** prgsczProductCodes,
334 __out DWORD* pcRelatedProducts,
335 __in BOOL fReturnHighestVersionOnly
336 );
337HRESULT DAPI WiuEnableLog(
338 __in DWORD dwLogMode,
339 __in_z LPCWSTR wzLogFile,
340 __in DWORD dwLogAttributes
341 );
342HRESULT DAPI WiuInitializeInternalUI(
343 __in INSTALLUILEVEL internalUILevel,
344 __in_opt HWND hwndParent,
345 __in WIU_MSI_EXECUTE_CONTEXT* pExecuteContext
346 );
347HRESULT DAPI WiuInitializeExternalUI(
348 __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler,
349 __in INSTALLUILEVEL internalUILevel,
350 __in_opt HWND hwndParent,
351 __in LPVOID pvContext,
352 __in BOOL fRollback,
353 __in WIU_MSI_EXECUTE_CONTEXT* pExecuteContext
354 );
355void DAPI WiuUninitializeExternalUI(
356 __in WIU_MSI_EXECUTE_CONTEXT* pExecuteContext
357 );
358HRESULT DAPI WiuConfigureProductEx(
359 __in_z LPCWSTR wzProduct,
360 __in int iInstallLevel,
361 __in INSTALLSTATE eInstallState,
362 __in_z LPCWSTR wzCommandLine,
363 __out WIU_RESTART* pRestart
364 );
365HRESULT DAPI WiuInstallProduct(
366 __in_z LPCWSTR wzPackagPath,
367 __in_z LPCWSTR wzCommandLine,
368 __out WIU_RESTART* pRestart
369 );
370HRESULT DAPI WiuRemovePatches(
371 __in_z LPCWSTR wzPatchList,
372 __in_z LPCWSTR wzProductCode,
373 __in_z LPCWSTR wzPropertyList,
374 __out WIU_RESTART* pRestart
375 );
376HRESULT DAPI WiuSourceListAddSourceEx(
377 __in_z LPCWSTR wzProductCodeOrPatchCode,
378 __in_z_opt LPCWSTR wzUserSid,
379 __in MSIINSTALLCONTEXT dwContext,
380 __in DWORD dwCode,
381 __in_z LPCWSTR wzSource,
382 __in_opt DWORD dwIndex
383 );
384HRESULT DAPI WiuBeginTransaction(
385 __in_z LPCWSTR szName,
386 __in DWORD dwTransactionAttributes,
387 __out MSIHANDLE* phTransactionHandle,
388 __out HANDLE* phChangeOfOwnerEvent,
389 __in DWORD dwLogMode,
390 __in_z LPCWSTR szLogPath
391 );
392HRESULT DAPI WiuEndTransaction(
393 __in DWORD dwTransactionState,
394 __in DWORD dwLogMode,
395 __in_z LPCWSTR szLogPath
396 );
397BOOL DAPI WiuIsMsiTransactionSupported(
398 );
399
400#ifdef __cplusplus
401}
402#endif
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/wuautil.h b/src/libs/dutil/WixToolset.DUtil/inc/wuautil.h
new file mode 100644
index 00000000..b239c4e6
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/wuautil.h
@@ -0,0 +1,19 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9HRESULT DAPI WuaPauseAutomaticUpdates();
10
11HRESULT DAPI WuaResumeAutomaticUpdates();
12
13HRESULT DAPI WuaRestartRequired(
14 __out BOOL* pfRestartRequired
15 );
16
17#if defined(__cplusplus)
18}
19#endif
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/xmlutil.h b/src/libs/dutil/WixToolset.DUtil/inc/xmlutil.h
new file mode 100644
index 00000000..ba92ada9
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inc/xmlutil.h
@@ -0,0 +1,167 @@
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
5extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument = {0x2933BF90, 0x7B36, 0x11d2, {0xB2, 0x0E, 0x00, 0xC0, 0x4F, 0x98, 0x3E, 0x60}};
6extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument20 = {0xF6D90F11, 0x9C73, 0x11D3, {0xB3, 0x2E, 0x00, 0xC0, 0x4F, 0x99, 0x0B, 0xB4}};
7extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument26 = {0xf5078f1b, 0xc551, 0x11d3, {0x89, 0xb9, 0x00, 0x00, 0xf8, 0x1f, 0xe2, 0x21}};
8extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument30 = {0xf5078f32, 0xc551, 0x11d3, {0x89, 0xb9, 0x00, 0x00, 0xf8, 0x1f, 0xe2, 0x21}};
9extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument40 = {0x88d969c0, 0xf192, 0x11d4, {0xa6, 0x5f, 0x00, 0x40, 0x96, 0x32, 0x51, 0xe5}};
10extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument50 = {0x88d969e5, 0xf192, 0x11d4, {0xa6, 0x5f, 0x00, 0x40, 0x96, 0x32, 0x51, 0xe5}};
11extern __declspec(selectany) const CLSID XmlUtil_CLSID_DOMDocument60 = {0x88d96a05, 0xf192, 0x11d4, {0xa6, 0x5f, 0x00, 0x40, 0x96, 0x32, 0x51, 0xe5}};
12extern __declspec(selectany) const CLSID XmlUtil_CLSID_XMLSchemaCache = {0x88d969c2, 0xf192, 0x11d4, {0xa6, 0x5f, 0x00, 0x40, 0x96, 0x32, 0x51, 0xe5}};
13
14extern __declspec(selectany) const IID XmlUtil_IID_IXMLDOMDocument = {0x2933BF81, 0x7B36, 0x11D2, {0xB2, 0x0E, 0x00, 0xC0, 0x4F, 0x98, 0x3E, 0x60}};
15extern __declspec(selectany) const IID XmlUtil_IID_IXMLDOMDocument2 = {0x2933BF95, 0x7B36, 0x11D2, {0xB2, 0x0E, 0x00, 0xC0, 0x4F, 0x98, 0x3E, 0x60}};
16extern __declspec(selectany) const IID XmlUtil_IID_IXMLDOMSchemaCollection = {0x373984C8, 0xB845, 0x449B, {0x91, 0xE7, 0x45, 0xAC, 0x83, 0x03, 0x6A, 0xDE}};
17
18typedef enum XML_LOAD_ATTRIBUTE
19{
20 XML_LOAD_PRESERVE_WHITESPACE = 1,
21} XML_LOAD_ATTRIBUTE;
22
23
24#ifdef __cplusplus
25extern "C" {
26#endif
27
28HRESULT DAPI XmlInitialize();
29void DAPI XmlUninitialize();
30
31HRESULT DAPI XmlCreateElement(
32 __in IXMLDOMDocument *pixdDocument,
33 __in_z LPCWSTR wzElementName,
34 __out IXMLDOMElement **ppixnElement
35 );
36HRESULT DAPI XmlCreateDocument(
37 __in_opt LPCWSTR pwzElementName,
38 __out IXMLDOMDocument** ppixdDocument,
39 __out_opt IXMLDOMElement** ppixeRootElement = NULL
40 );
41HRESULT DAPI XmlLoadDocument(
42 __in_z LPCWSTR wzDocument,
43 __out IXMLDOMDocument** ppixdDocument
44 );
45HRESULT DAPI XmlLoadDocumentEx(
46 __in_z LPCWSTR wzDocument,
47 __in DWORD dwAttributes,
48 __out IXMLDOMDocument** ppixdDocument
49 );
50HRESULT DAPI XmlLoadDocumentFromFile(
51 __in_z LPCWSTR wzPath,
52 __out IXMLDOMDocument** ppixdDocument
53 );
54HRESULT DAPI XmlLoadDocumentFromBuffer(
55 __in_bcount(cbSource) const BYTE* pbSource,
56 __in SIZE_T cbSource,
57 __out IXMLDOMDocument** ppixdDocument
58 );
59HRESULT DAPI XmlLoadDocumentFromFileEx(
60 __in_z LPCWSTR wzPath,
61 __in DWORD dwAttributes,
62 __out IXMLDOMDocument** ppixdDocument
63 );
64HRESULT DAPI XmlSelectSingleNode(
65 __in IXMLDOMNode* pixnParent,
66 __in_z LPCWSTR wzXPath,
67 __out IXMLDOMNode **ppixnChild
68 );
69HRESULT DAPI XmlSetAttribute(
70 __in IXMLDOMNode* pixnNode,
71 __in_z LPCWSTR pwzAttribute,
72 __in_z LPCWSTR pwzAttributeValue
73 );
74HRESULT DAPI XmlCreateTextNode(
75 __in IXMLDOMDocument *pixdDocument,
76 __in_z LPCWSTR wzText,
77 __out IXMLDOMText **ppixnTextNode
78 );
79HRESULT DAPI XmlGetText(
80 __in IXMLDOMNode* pixnNode,
81 __deref_out_z BSTR* pbstrText
82 );
83HRESULT DAPI XmlGetAttribute(
84 __in IXMLDOMNode* pixnNode,
85 __in_z LPCWSTR pwzAttribute,
86 __deref_out_z BSTR* pbstrAttributeValue
87 );
88HRESULT DAPI XmlGetAttributeEx(
89 __in IXMLDOMNode* pixnNode,
90 __in_z LPCWSTR wzAttribute,
91 __deref_out_z LPWSTR* psczAttributeValue
92 );
93HRESULT DAPI XmlGetYesNoAttribute(
94 __in IXMLDOMNode* pixnNode,
95 __in_z LPCWSTR wzAttribute,
96 __out BOOL* pfYes
97 );
98HRESULT DAPI XmlGetAttributeNumber(
99 __in IXMLDOMNode* pixnNode,
100 __in_z LPCWSTR pwzAttribute,
101 __out DWORD* pdwValue
102 );
103HRESULT DAPI XmlGetAttributeNumberBase(
104 __in IXMLDOMNode* pixnNode,
105 __in_z LPCWSTR pwzAttribute,
106 __in int nBase,
107 __out DWORD* pdwValue
108 );
109HRESULT DAPI XmlGetAttributeLargeNumber(
110 __in IXMLDOMNode* pixnNode,
111 __in_z LPCWSTR pwzAttribute,
112 __out DWORD64* pdw64Value
113 );
114HRESULT DAPI XmlGetNamedItem(
115 __in IXMLDOMNamedNodeMap *pixnmAttributes,
116 __in_opt LPCWSTR wzName,
117 __out IXMLDOMNode **ppixnNamedItem
118 );
119HRESULT DAPI XmlSetText(
120 __in IXMLDOMNode* pixnNode,
121 __in_z LPCWSTR pwzText
122 );
123HRESULT DAPI XmlSetTextNumber(
124 __in IXMLDOMNode *pixnNode,
125 __in DWORD dwValue
126 );
127HRESULT DAPI XmlCreateChild(
128 __in IXMLDOMNode* pixnParent,
129 __in_z LPCWSTR pwzElementType,
130 __out IXMLDOMNode** ppixnChild
131 );
132HRESULT DAPI XmlRemoveAttribute(
133 __in IXMLDOMNode* pixnNode,
134 __in_z LPCWSTR pwzAttribute
135 );
136HRESULT DAPI XmlSelectNodes(
137 __in IXMLDOMNode* pixnParent,
138 __in_z LPCWSTR wzXPath,
139 __out IXMLDOMNodeList **ppixnChild
140 );
141HRESULT DAPI XmlNextAttribute(
142 __in IXMLDOMNamedNodeMap* pixnnm,
143 __out IXMLDOMNode** pixnAttribute,
144 __deref_opt_out_z_opt BSTR* pbstrAttribute
145 );
146HRESULT DAPI XmlNextElement(
147 __in IXMLDOMNodeList* pixnl,
148 __out IXMLDOMNode** pixnElement,
149 __deref_opt_out_z_opt BSTR* pbstrElement
150 );
151HRESULT DAPI XmlRemoveChildren(
152 __in IXMLDOMNode* pixnSource,
153 __in_z LPCWSTR pwzXPath
154 );
155HRESULT DAPI XmlSaveDocument(
156 __in IXMLDOMDocument* pixdDocument,
157 __inout LPCWSTR wzPath
158 );
159HRESULT DAPI XmlSaveDocumentToBuffer(
160 __in IXMLDOMDocument* pixdDocument,
161 __deref_out_bcount(*pcbDest) BYTE** ppbDest,
162 __out DWORD* pcbDest
163 );
164
165#ifdef __cplusplus
166}
167#endif
diff --git a/src/libs/dutil/WixToolset.DUtil/inetutil.cpp b/src/libs/dutil/WixToolset.DUtil/inetutil.cpp
new file mode 100644
index 00000000..8dace55f
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/inetutil.cpp
@@ -0,0 +1,155 @@
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// Exit macros
7#define InetExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_INETUTIL, x, s, __VA_ARGS__)
8#define InetExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_INETUTIL, x, s, __VA_ARGS__)
9#define InetExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_INETUTIL, x, s, __VA_ARGS__)
10#define InetExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_INETUTIL, x, s, __VA_ARGS__)
11#define InetExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_INETUTIL, x, s, __VA_ARGS__)
12#define InetExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_INETUTIL, x, s, __VA_ARGS__)
13#define InetExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_INETUTIL, p, x, e, s, __VA_ARGS__)
14#define InetExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_INETUTIL, p, x, s, __VA_ARGS__)
15#define InetExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_INETUTIL, p, x, e, s, __VA_ARGS__)
16#define InetExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_INETUTIL, p, x, s, __VA_ARGS__)
17#define InetExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_INETUTIL, e, x, s, __VA_ARGS__)
18#define InetExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_INETUTIL, g, x, s, __VA_ARGS__)
19
20
21/*******************************************************************
22 InternetGetSizeByHandle - returns size of file by url handle
23
24*******************************************************************/
25extern "C" HRESULT DAPI InternetGetSizeByHandle(
26 __in HINTERNET hiFile,
27 __out LONGLONG* pllSize
28 )
29{
30 Assert(pllSize);
31
32 HRESULT hr = S_OK;
33 DWORD dwSize = 0;
34 DWORD cb = 0;
35
36 cb = sizeof(dwSize);
37 if (!::HttpQueryInfoW(hiFile, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, reinterpret_cast<LPVOID>(&dwSize), &cb, NULL))
38 {
39 InetExitOnLastError(hr, "Failed to get size for internet file handle");
40 }
41
42 *pllSize = dwSize;
43LExit:
44 return hr;
45}
46
47
48/*******************************************************************
49 InetGetCreateTimeByHandle - returns url creation time
50
51******************************************************************/
52extern "C" HRESULT DAPI InternetGetCreateTimeByHandle(
53 __in HINTERNET hiFile,
54 __out LPFILETIME pft
55 )
56{
57 Assert(pft);
58
59 HRESULT hr = S_OK;
60 SYSTEMTIME st = {0 };
61 DWORD cb = sizeof(SYSTEMTIME);
62
63 if (!::HttpQueryInfoW(hiFile, HTTP_QUERY_LAST_MODIFIED | HTTP_QUERY_FLAG_SYSTEMTIME, reinterpret_cast<LPVOID>(&st), &cb, NULL))
64 {
65 InetExitWithLastError(hr, "failed to get create time for internet file handle");
66 }
67
68 if (!::SystemTimeToFileTime(&st, pft))
69 {
70 InetExitWithLastError(hr, "failed to convert system time to file time");
71 }
72
73LExit:
74 return hr;
75}
76
77
78/*******************************************************************
79 InternetQueryInfoString - query info string
80
81*******************************************************************/
82extern "C" HRESULT DAPI InternetQueryInfoString(
83 __in HINTERNET hRequest,
84 __in DWORD dwInfo,
85 __deref_out_z LPWSTR* psczValue
86 )
87{
88 HRESULT hr = S_OK;
89 SIZE_T cbOriginal = 0;
90 DWORD cbValue = 0;
91 DWORD dwIndex = 0;
92
93 // If nothing was provided start off with some arbitrary size.
94 if (!*psczValue)
95 {
96 hr = StrAlloc(psczValue, 64);
97 InetExitOnFailure(hr, "Failed to allocate memory for value.");
98 }
99
100 hr = StrSize(*psczValue, &cbOriginal);
101 InetExitOnFailure(hr, "Failed to get size of value.");
102
103 cbValue = (DWORD)min(DWORD_MAX, cbOriginal);
104
105 if (!::HttpQueryInfoW(hRequest, dwInfo, static_cast<void*>(*psczValue), &cbValue, &dwIndex))
106 {
107 DWORD er = ::GetLastError();
108 if (ERROR_INSUFFICIENT_BUFFER == er)
109 {
110 cbValue += sizeof(WCHAR); // add one character for the null terminator.
111
112 hr = StrAlloc(psczValue, cbValue / sizeof(WCHAR));
113 InetExitOnFailure(hr, "Failed to allocate value.");
114
115 if (!::HttpQueryInfoW(hRequest, dwInfo, static_cast<void*>(*psczValue), &cbValue, &dwIndex))
116 {
117 er = ::GetLastError();
118 }
119 else
120 {
121 er = ERROR_SUCCESS;
122 }
123 }
124
125 hr = HRESULT_FROM_WIN32(er);
126 InetExitOnRootFailure(hr, "Failed to get query information.");
127 }
128
129LExit:
130 return hr;
131}
132
133
134/*******************************************************************
135 InternetQueryInfoNumber - query info number
136
137*******************************************************************/
138extern "C" HRESULT DAPI InternetQueryInfoNumber(
139 __in HINTERNET hRequest,
140 __in DWORD dwInfo,
141 __inout LONG* plInfo
142 )
143{
144 HRESULT hr = S_OK;
145 DWORD cbCode = sizeof(LONG);
146 DWORD dwIndex = 0;
147
148 if (!::HttpQueryInfoW(hRequest, dwInfo | HTTP_QUERY_FLAG_NUMBER, static_cast<void*>(plInfo), &cbCode, &dwIndex))
149 {
150 InetExitWithLastError(hr, "Failed to get query information.");
151 }
152
153LExit:
154 return hr;
155}
diff --git a/src/libs/dutil/WixToolset.DUtil/iniutil.cpp b/src/libs/dutil/WixToolset.DUtil/iniutil.cpp
new file mode 100644
index 00000000..70b62995
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/iniutil.cpp
@@ -0,0 +1,768 @@
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// Exit macros
7#define IniExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_INIUTIL, x, s, __VA_ARGS__)
8#define IniExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_INIUTIL, x, s, __VA_ARGS__)
9#define IniExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_INIUTIL, x, s, __VA_ARGS__)
10#define IniExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_INIUTIL, x, s, __VA_ARGS__)
11#define IniExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_INIUTIL, x, s, __VA_ARGS__)
12#define IniExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_INIUTIL, x, s, __VA_ARGS__)
13#define IniExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_INIUTIL, p, x, e, s, __VA_ARGS__)
14#define IniExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_INIUTIL, p, x, s, __VA_ARGS__)
15#define IniExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_INIUTIL, p, x, e, s, __VA_ARGS__)
16#define IniExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_INIUTIL, p, x, s, __VA_ARGS__)
17#define IniExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_INIUTIL, e, x, s, __VA_ARGS__)
18#define IniExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_INIUTIL, g, x, s, __VA_ARGS__)
19
20const LPCWSTR wzSectionSeparator = L"\\";
21
22struct INI_STRUCT
23{
24 LPWSTR sczPath; // the path to the INI file to be parsed
25
26 LPWSTR sczOpenTagPrefix; // For regular ini, this would be '['
27 LPWSTR sczOpenTagPostfix; // For regular ini, this would be ']'
28
29 LPWSTR sczValuePrefix; // for regular ini, this would be NULL
30 LPWSTR sczValueSeparator; // for regular ini, this would be '='
31
32 LPWSTR *rgsczValueSeparatorExceptions;
33 DWORD cValueSeparatorExceptions;
34
35 LPWSTR sczCommentLinePrefix; // for regular ini, this would be ';'
36
37 INI_VALUE *rgivValues;
38 DWORD cValues;
39
40 LPWSTR *rgsczLines;
41 DWORD cLines;
42
43 FILE_ENCODING feEncoding;
44 BOOL fModified;
45};
46
47const int INI_HANDLE_BYTES = sizeof(INI_STRUCT);
48
49static HRESULT GetSectionPrefixFromName(
50 __in_z LPCWSTR wzName,
51 __deref_inout_z LPWSTR* psczOutput
52 );
53static void UninitializeIniValue(
54 INI_VALUE *pivValue
55 );
56
57extern "C" HRESULT DAPI IniInitialize(
58 __out_bcount(INI_HANDLE_BYTES) INI_HANDLE* piHandle
59 )
60{
61 HRESULT hr = S_OK;
62
63 // Allocate the handle
64 *piHandle = static_cast<INI_HANDLE>(MemAlloc(sizeof(INI_STRUCT), TRUE));
65 IniExitOnNull(*piHandle, hr, E_OUTOFMEMORY, "Failed to allocate ini object");
66
67LExit:
68 return hr;
69}
70
71extern "C" void DAPI IniUninitialize(
72 __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle
73 )
74{
75 INI_STRUCT *pi = static_cast<INI_STRUCT *>(piHandle);
76
77 ReleaseStr(pi->sczPath);
78 ReleaseStr(pi->sczOpenTagPrefix);
79 ReleaseStr(pi->sczOpenTagPostfix);
80 ReleaseStr(pi->sczValuePrefix);
81 ReleaseStr(pi->sczValueSeparator);
82
83 for (DWORD i = 0; i < pi->cValueSeparatorExceptions; ++i)
84 {
85 ReleaseStr(pi->rgsczValueSeparatorExceptions + i);
86 }
87
88 ReleaseStr(pi->sczCommentLinePrefix);
89
90 for (DWORD i = 0; i < pi->cValues; ++i)
91 {
92 UninitializeIniValue(pi->rgivValues + i);
93 }
94 ReleaseMem(pi->rgivValues);
95
96 ReleaseStrArray(pi->rgsczLines, pi->cLines);
97
98 ReleaseMem(pi);
99}
100
101extern "C" HRESULT DAPI IniSetOpenTag(
102 __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle,
103 __in_z_opt LPCWSTR wzOpenTagPrefix,
104 __in_z_opt LPCWSTR wzOpenTagPostfix
105 )
106{
107 HRESULT hr = S_OK;
108
109 INI_STRUCT *pi = static_cast<INI_STRUCT *>(piHandle);
110
111 if (wzOpenTagPrefix)
112 {
113 hr = StrAllocString(&pi->sczOpenTagPrefix, wzOpenTagPrefix, 0);
114 IniExitOnFailure(hr, "Failed to copy open tag prefix to ini struct: %ls", wzOpenTagPrefix);
115 }
116 else
117 {
118 ReleaseNullStr(pi->sczOpenTagPrefix);
119 }
120
121 if (wzOpenTagPostfix)
122 {
123 hr = StrAllocString(&pi->sczOpenTagPostfix, wzOpenTagPostfix, 0);
124 IniExitOnFailure(hr, "Failed to copy open tag postfix to ini struct: %ls", wzOpenTagPostfix);
125 }
126 else
127 {
128 ReleaseNullStr(pi->sczOpenTagPrefix);
129 }
130
131LExit:
132 return hr;
133}
134
135extern "C" HRESULT DAPI IniSetValueStyle(
136 __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle,
137 __in_z_opt LPCWSTR wzValuePrefix,
138 __in_z_opt LPCWSTR wzValueSeparator
139 )
140{
141 HRESULT hr = S_OK;
142
143 INI_STRUCT *pi = static_cast<INI_STRUCT *>(piHandle);
144
145 if (wzValuePrefix)
146 {
147 hr = StrAllocString(&pi->sczValuePrefix, wzValuePrefix, 0);
148 IniExitOnFailure(hr, "Failed to copy value prefix to ini struct: %ls", wzValuePrefix);
149 }
150 else
151 {
152 ReleaseNullStr(pi->sczValuePrefix);
153 }
154
155 if (wzValueSeparator)
156 {
157 hr = StrAllocString(&pi->sczValueSeparator, wzValueSeparator, 0);
158 IniExitOnFailure(hr, "Failed to copy value separator to ini struct: %ls", wzValueSeparator);
159 }
160 else
161 {
162 ReleaseNullStr(pi->sczValueSeparator);
163 }
164
165LExit:
166 return hr;
167}
168
169extern "C" HRESULT DAPI IniSetValueSeparatorException(
170 __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle,
171 __in_z LPCWSTR wzValueNamePrefix
172 )
173{
174 HRESULT hr = S_OK;
175 DWORD dwInsertedIndex = 0;
176
177 INI_STRUCT *pi = static_cast<INI_STRUCT *>(piHandle);
178
179 hr = MemEnsureArraySize(reinterpret_cast<void **>(&pi->rgsczValueSeparatorExceptions), pi->cValueSeparatorExceptions + 1, sizeof(LPWSTR), 5);
180 IniExitOnFailure(hr, "Failed to increase array size for value separator exceptions");
181 dwInsertedIndex = pi->cValueSeparatorExceptions;
182 ++pi->cValueSeparatorExceptions;
183
184 hr = StrAllocString(&pi->rgsczValueSeparatorExceptions[dwInsertedIndex], wzValueNamePrefix, 0);
185 IniExitOnFailure(hr, "Failed to copy value separator exception");
186
187LExit:
188 return hr;
189}
190
191extern "C" HRESULT DAPI IniSetCommentStyle(
192 __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle,
193 __in_z_opt LPCWSTR wzLinePrefix
194 )
195{
196 HRESULT hr = S_OK;
197
198 INI_STRUCT *pi = static_cast<INI_STRUCT *>(piHandle);
199
200 if (wzLinePrefix)
201 {
202 hr = StrAllocString(&pi->sczCommentLinePrefix, wzLinePrefix, 0);
203 IniExitOnFailure(hr, "Failed to copy comment line prefix to ini struct: %ls", wzLinePrefix);
204 }
205 else
206 {
207 ReleaseNullStr(pi->sczCommentLinePrefix);
208 }
209
210LExit:
211 return hr;
212}
213
214extern "C" HRESULT DAPI IniParse(
215 __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle,
216 __in LPCWSTR wzPath,
217 __out_opt FILE_ENCODING *pfeEncodingFound
218 )
219{
220 HRESULT hr = S_OK;
221 DWORD dwValuePrefixLength = 0;
222 DWORD dwValueSeparatorExceptionLength = 0;
223 LPWSTR sczContents = NULL;
224 LPWSTR sczCurrentSection = NULL;
225 LPWSTR sczName = NULL;
226 LPWSTR sczNameTrimmed = NULL;
227 LPWSTR sczValue = NULL;
228 LPWSTR sczValueTrimmed = NULL;
229 LPWSTR wzOpenTagPrefix = NULL;
230 LPWSTR wzOpenTagPostfix = NULL;
231 LPWSTR wzValuePrefix = NULL;
232 LPWSTR wzValueNameStart = NULL;
233 LPWSTR wzValueSeparator = NULL;
234 LPWSTR wzCommentLinePrefix = NULL;
235 LPWSTR wzValueBegin = NULL;
236 LPCWSTR wzTemp = NULL;
237
238 INI_STRUCT *pi = static_cast<INI_STRUCT *>(piHandle);
239
240 BOOL fSections = (NULL != pi->sczOpenTagPrefix) && (NULL != pi->sczOpenTagPostfix);
241 BOOL fValuePrefix = (NULL != pi->sczValuePrefix);
242
243 hr = StrAllocString(&pi->sczPath, wzPath, 0);
244 IniExitOnFailure(hr, "Failed to copy path to ini struct: %ls", wzPath);
245
246 hr = FileToString(pi->sczPath, &sczContents, &pi->feEncoding);
247 IniExitOnFailure(hr, "Failed to convert file to string: %ls", pi->sczPath);
248
249 if (pfeEncodingFound)
250 {
251 *pfeEncodingFound = pi->feEncoding;
252 }
253
254 if (!sczContents || !*sczContents)
255 {
256 // Empty string, nothing to parse
257 ExitFunction1(hr = S_OK);
258 }
259
260 dwValuePrefixLength = lstrlenW(pi->sczValuePrefix);
261 hr = StrSplitAllocArray(&pi->rgsczLines, reinterpret_cast<UINT *>(&pi->cLines), sczContents, L"\n");
262 IniExitOnFailure(hr, "Failed to split INI file into lines");
263
264 for (DWORD i = 0; i < pi->cLines; ++i)
265 {
266 if (!*pi->rgsczLines[i] || '\r' == *pi->rgsczLines[i])
267 {
268 continue;
269 }
270
271 if (pi->sczCommentLinePrefix)
272 {
273 wzCommentLinePrefix = wcsstr(pi->rgsczLines[i], pi->sczCommentLinePrefix);
274
275 if (wzCommentLinePrefix && wzCommentLinePrefix <= pi->rgsczLines[i] + 1)
276 {
277 continue;
278 }
279 }
280
281 if (pi->sczOpenTagPrefix)
282 {
283 wzOpenTagPrefix = wcsstr(pi->rgsczLines[i], pi->sczOpenTagPrefix);
284 if (wzOpenTagPrefix)
285 {
286 // If there is an open tag prefix but there is anything but whitespace before it, then it's NOT an open tag prefix
287 // This is important, for example, to support values with names like "Array[0]=blah" in INI format
288 for (wzTemp = pi->rgsczLines[i]; wzTemp < wzOpenTagPrefix; ++wzTemp)
289 {
290 if (*wzTemp != L' ' && *wzTemp != L'\t')
291 {
292 wzOpenTagPrefix = NULL;
293 break;
294 }
295 }
296 }
297 }
298
299 if (pi->sczOpenTagPostfix)
300 {
301 wzOpenTagPostfix = wcsstr(pi->rgsczLines[i], pi->sczOpenTagPostfix);
302 }
303
304 if (pi->sczValuePrefix)
305 {
306 wzValuePrefix = wcsstr(pi->rgsczLines[i], pi->sczValuePrefix);
307 if (wzValuePrefix != NULL)
308 {
309 wzValueNameStart = wzValuePrefix + dwValuePrefixLength;
310 }
311 }
312 else
313 {
314 wzValueNameStart = pi->rgsczLines[i];
315 }
316
317 if (pi->sczValueSeparator && NULL != wzValueNameStart && *wzValueNameStart != L'\0')
318 {
319 dwValueSeparatorExceptionLength = 0;
320 for (DWORD j = 0; j < pi->cValueSeparatorExceptions; ++j)
321 {
322 if (pi->rgsczLines[i] == wcsstr(pi->rgsczLines[i], pi->rgsczValueSeparatorExceptions[j]))
323 {
324 dwValueSeparatorExceptionLength = lstrlenW(pi->rgsczValueSeparatorExceptions[j]);
325 break;
326 }
327 }
328
329 wzValueSeparator = wcsstr(wzValueNameStart + dwValueSeparatorExceptionLength, pi->sczValueSeparator);
330 }
331
332 // Don't keep the endline
333 if (pi->rgsczLines[i][lstrlenW(pi->rgsczLines[i])-1] == L'\r')
334 {
335 pi->rgsczLines[i][lstrlenW(pi->rgsczLines[i])-1] = L'\0';
336 }
337
338 if (fSections && wzOpenTagPrefix && wzOpenTagPostfix && wzOpenTagPrefix < wzOpenTagPostfix && (NULL == wzCommentLinePrefix || wzOpenTagPrefix < wzCommentLinePrefix))
339 {
340 // There is an section starting here, let's keep track of it and move on
341 hr = StrAllocString(&sczCurrentSection, wzOpenTagPrefix + lstrlenW(pi->sczOpenTagPrefix), wzOpenTagPostfix - (wzOpenTagPrefix + lstrlenW(pi->sczOpenTagPrefix)));
342 IniExitOnFailure(hr, "Failed to record section name for line: %ls of INI file: %ls", pi->rgsczLines[i], pi->sczPath);
343
344 // Sections will be calculated dynamically after any set operations, so don't include this in the list of lines to remember for output
345 ReleaseNullStr(pi->rgsczLines[i]);
346 }
347 else if (wzValueSeparator && (NULL == wzCommentLinePrefix || wzValueSeparator < wzCommentLinePrefix)
348 && (!fValuePrefix || wzValuePrefix))
349 {
350 if (fValuePrefix)
351 {
352 wzValueBegin = wzValuePrefix + lstrlenW(pi->sczValuePrefix);
353 }
354 else
355 {
356 wzValueBegin = pi->rgsczLines[i];
357 }
358
359 hr = MemEnsureArraySize(reinterpret_cast<void **>(&pi->rgivValues), pi->cValues + 1, sizeof(INI_VALUE), 100);
360 IniExitOnFailure(hr, "Failed to increase array size for value array");
361
362 if (sczCurrentSection)
363 {
364 hr = StrAllocString(&sczName, sczCurrentSection, 0);
365 IniExitOnFailure(hr, "Failed to copy current section name");
366
367 hr = StrAllocConcat(&sczName, wzSectionSeparator, 0);
368 IniExitOnFailure(hr, "Failed to copy current section name");
369 }
370
371 hr = StrAllocConcat(&sczName, wzValueBegin, wzValueSeparator - wzValueBegin);
372 IniExitOnFailure(hr, "Failed to copy name");
373
374 hr = StrAllocString(&sczValue, wzValueSeparator + lstrlenW(pi->sczValueSeparator), 0);
375 IniExitOnFailure(hr, "Failed to copy value");
376
377 hr = StrTrimWhitespace(&sczNameTrimmed, sczName);
378 IniExitOnFailure(hr, "Failed to trim whitespace from name");
379
380 hr = StrTrimWhitespace(&sczValueTrimmed, sczValue);
381 IniExitOnFailure(hr, "Failed to trim whitespace from value");
382
383 pi->rgivValues[pi->cValues].wzName = const_cast<LPCWSTR>(sczNameTrimmed);
384 sczNameTrimmed = NULL;
385 pi->rgivValues[pi->cValues].wzValue = const_cast<LPCWSTR>(sczValueTrimmed);
386 sczValueTrimmed = NULL;
387 pi->rgivValues[pi->cValues].dwLineNumber = i + 1;
388
389 ++pi->cValues;
390
391 // Values will be calculated dynamically after any set operations, so don't include this in the list of lines to remember for output
392 ReleaseNullStr(pi->rgsczLines[i]);
393 }
394 else
395 {
396 // Must be a comment, so ignore it and keep it in the list to output
397 }
398
399 ReleaseNullStr(sczName);
400 }
401
402LExit:
403 ReleaseStr(sczCurrentSection);
404 ReleaseStr(sczContents);
405 ReleaseStr(sczName);
406 ReleaseStr(sczNameTrimmed);
407 ReleaseStr(sczValue);
408 ReleaseStr(sczValueTrimmed);
409
410 return hr;
411}
412
413extern "C" HRESULT DAPI IniGetValueList(
414 __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle,
415 __deref_out_ecount_opt(*pcValues) INI_VALUE** prgivValues,
416 __out DWORD *pcValues
417 )
418{
419 HRESULT hr = S_OK;
420
421 INI_STRUCT *pi = static_cast<INI_STRUCT *>(piHandle);
422
423 *prgivValues = pi->rgivValues;
424 *pcValues = pi->cValues;
425
426 return hr;
427}
428
429extern "C" HRESULT DAPI IniGetValue(
430 __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle,
431 __in LPCWSTR wzValueName,
432 __deref_out_z LPWSTR* psczValue
433 )
434{
435 HRESULT hr = S_OK;
436
437 INI_STRUCT *pi = static_cast<INI_STRUCT *>(piHandle);
438 INI_VALUE *pValue = NULL;
439
440 for (DWORD i = 0; i < pi->cValues; ++i)
441 {
442 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pi->rgivValues[i].wzName, -1, wzValueName, -1))
443 {
444 pValue = pi->rgivValues + i;
445 break;
446 }
447 }
448
449 if (NULL == pValue)
450 {
451 hr = E_NOTFOUND;
452 IniExitOnFailure(hr, "Failed to check for INI value: %ls", wzValueName);
453 }
454
455 if (NULL == pValue->wzValue)
456 {
457 ExitFunction1(hr = E_NOTFOUND);
458 }
459
460 hr = StrAllocString(psczValue, pValue->wzValue, 0);
461 IniExitOnFailure(hr, "Failed to make copy of value while looking up INI value named: %ls", wzValueName);
462
463LExit:
464 return hr;
465}
466
467extern "C" HRESULT DAPI IniSetValue(
468 __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle,
469 __in LPCWSTR wzValueName,
470 __in_z_opt LPCWSTR wzValue
471 )
472{
473 HRESULT hr = S_OK;
474 LPWSTR sczSectionPrefix = NULL; // includes section name and backslash
475 LPWSTR sczName = NULL;
476 LPWSTR sczValue = NULL;
477 DWORD dwInsertIndex = DWORD_MAX;
478
479 INI_STRUCT *pi = static_cast<INI_STRUCT *>(piHandle);
480 INI_VALUE *pValue = NULL;
481
482 for (DWORD i = 0; i < pi->cValues; ++i)
483 {
484 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pi->rgivValues[i].wzName, -1, wzValueName, -1))
485 {
486 pValue = pi->rgivValues + i;
487 break;
488 }
489 }
490
491 // We're killing the value
492 if (NULL == wzValue)
493 {
494 if (pValue && pValue->wzValue)
495 {
496 pi->fModified = TRUE;
497 sczValue = const_cast<LPWSTR>(pValue->wzValue);
498 pValue->wzValue = NULL;
499 ReleaseNullStr(sczValue);
500 }
501
502 ExitFunction();
503 }
504 else
505 {
506 if (pValue)
507 {
508 if (CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, 0, pValue->wzValue, -1, wzValue, -1))
509 {
510 pi->fModified = TRUE;
511 hr = StrAllocString(const_cast<LPWSTR *>(&pValue->wzValue), wzValue, 0);
512 IniExitOnFailure(hr, "Failed to update value INI value named: %ls", wzValueName);
513 }
514
515 ExitFunction1(hr = S_OK);
516 }
517 else
518 {
519 if (wzValueName)
520 {
521 hr = GetSectionPrefixFromName(wzValueName, &sczSectionPrefix);
522 IniExitOnFailure(hr, "Failed to get section prefix from value name: %ls", wzValueName);
523 }
524
525 // If we have a section prefix, figure out the index to insert it (at the end of the section it belongs in)
526 if (sczSectionPrefix)
527 {
528 for (DWORD i = 0; i < pi->cValues; ++i)
529 {
530 if (0 == wcsncmp(pi->rgivValues[i].wzName, sczSectionPrefix, lstrlenW(sczSectionPrefix)))
531 {
532 dwInsertIndex = i;
533 }
534 else if (DWORD_MAX != dwInsertIndex)
535 {
536 break;
537 }
538 }
539 }
540 else
541 {
542 for (DWORD i = 0; i < pi->cValues; ++i)
543 {
544 if (NULL == wcsstr(pi->rgivValues[i].wzName, wzSectionSeparator))
545 {
546 dwInsertIndex = i;
547 }
548 else if (DWORD_MAX != dwInsertIndex)
549 {
550 break;
551 }
552 }
553 }
554
555 // Otherwise, just add it to the end
556 if (DWORD_MAX == dwInsertIndex)
557 {
558 dwInsertIndex = pi->cValues;
559 }
560
561 pi->fModified = TRUE;
562 hr = MemInsertIntoArray(reinterpret_cast<void **>(&pi->rgivValues), dwInsertIndex, 1, pi->cValues + 1, sizeof(INI_VALUE), 100);
563 IniExitOnFailure(hr, "Failed to insert value into array");
564
565 hr = StrAllocString(&sczName, wzValueName, 0);
566 IniExitOnFailure(hr, "Failed to copy name");
567
568 hr = StrAllocString(&sczValue, wzValue, 0);
569 IniExitOnFailure(hr, "Failed to copy value");
570
571 pi->rgivValues[dwInsertIndex].wzName = const_cast<LPCWSTR>(sczName);
572 sczName = NULL;
573 pi->rgivValues[dwInsertIndex].wzValue = const_cast<LPCWSTR>(sczValue);
574 sczValue = NULL;
575
576 ++pi->cValues;
577 }
578 }
579
580LExit:
581 ReleaseStr(sczName);
582 ReleaseStr(sczValue);
583
584 return hr;
585}
586
587extern "C" HRESULT DAPI IniWriteFile(
588 __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle,
589 __in_z_opt LPCWSTR wzPath,
590 __in FILE_ENCODING feOverrideEncoding
591 )
592{
593 HRESULT hr = S_OK;
594 LPWSTR sczCurrentSectionPrefix = NULL;
595 LPWSTR sczNewSectionPrefix = NULL;
596 LPWSTR sczContents = NULL;
597 LPCWSTR wzName = NULL;
598 DWORD dwLineArrayIndex = 1;
599 FILE_ENCODING feEncoding;
600
601 INI_STRUCT *pi = static_cast<INI_STRUCT *>(piHandle);
602
603 if (FILE_ENCODING_UNSPECIFIED == feOverrideEncoding)
604 {
605 feEncoding = pi->feEncoding;
606 }
607 else
608 {
609 feEncoding = feOverrideEncoding;
610 }
611
612 if (FILE_ENCODING_UNSPECIFIED == feEncoding)
613 {
614 feEncoding = FILE_ENCODING_UTF16_WITH_BOM;
615 }
616
617 if (!pi->fModified)
618 {
619 ExitFunction1(hr = S_OK);
620 }
621 if (NULL == wzPath && NULL == pi->sczPath)
622 {
623 ExitFunction1(hr = E_NOTFOUND);
624 }
625
626 BOOL fSections = (pi->sczOpenTagPrefix) && (pi->sczOpenTagPostfix);
627
628 hr = StrAllocString(&sczContents, L"", 0);
629 IniExitOnFailure(hr, "Failed to begin contents string as empty string");
630
631 // Insert any beginning lines we didn't understand like comments
632 if (0 < pi->cLines)
633 {
634 while (pi->rgsczLines[dwLineArrayIndex])
635 {
636 hr = StrAllocConcat(&sczContents, pi->rgsczLines[dwLineArrayIndex], 0);
637 IniExitOnFailure(hr, "Failed to add previous line to ini output buffer in-memory");
638
639 hr = StrAllocConcat(&sczContents, L"\r\n", 2);
640 IniExitOnFailure(hr, "Failed to add endline to ini output buffer in-memory");
641
642 ++dwLineArrayIndex;
643 }
644 }
645
646 for (DWORD i = 0; i < pi->cValues; ++i)
647 {
648 // Skip if this value was killed off
649 if (NULL == pi->rgivValues[i].wzValue)
650 {
651 continue;
652 }
653
654 // Now generate any lines for the current value like value line and maybe also a new section line before it
655
656 // First see if we need to write a section line
657 hr = GetSectionPrefixFromName(pi->rgivValues[i].wzName, &sczNewSectionPrefix);
658 IniExitOnFailure(hr, "Failed to get section prefix from name: %ls", pi->rgivValues[i].wzName);
659
660 // If the new section prefix is different, write a section out for it
661 if (fSections && sczNewSectionPrefix && (NULL == sczCurrentSectionPrefix || CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, 0, sczNewSectionPrefix, -1, sczCurrentSectionPrefix, -1)))
662 {
663 hr = StrAllocConcat(&sczContents, pi->sczOpenTagPrefix, 0);
664 IniExitOnFailure(hr, "Failed to concat open tag prefix to string");
665
666 // Exclude section separator (i.e. backslash) from new section prefix
667 hr = StrAllocConcat(&sczContents, sczNewSectionPrefix, lstrlenW(sczNewSectionPrefix)-lstrlenW(wzSectionSeparator));
668 IniExitOnFailure(hr, "Failed to concat section name to string");
669
670 hr = StrAllocConcat(&sczContents, pi->sczOpenTagPostfix, 0);
671 IniExitOnFailure(hr, "Failed to concat open tag postfix to string");
672
673 hr = StrAllocConcat(&sczContents, L"\r\n", 2);
674 IniExitOnFailure(hr, "Failed to add endline to ini output buffer in-memory");
675
676 ReleaseNullStr(sczCurrentSectionPrefix);
677 sczCurrentSectionPrefix = sczNewSectionPrefix;
678 sczNewSectionPrefix = NULL;
679 }
680
681 // Inserting lines we read before the current value if appropriate
682 while (pi->rgivValues[i].dwLineNumber > dwLineArrayIndex)
683 {
684 // Skip any lines were purposely forgot
685 if (NULL == pi->rgsczLines[dwLineArrayIndex])
686 {
687 ++dwLineArrayIndex;
688 continue;
689 }
690
691 hr = StrAllocConcat(&sczContents, pi->rgsczLines[dwLineArrayIndex++], 0);
692 IniExitOnFailure(hr, "Failed to add previous line to ini output buffer in-memory");
693
694 hr = StrAllocConcat(&sczContents, L"\r\n", 2);
695 IniExitOnFailure(hr, "Failed to add endline to ini output buffer in-memory");
696 }
697
698 wzName = pi->rgivValues[i].wzName;
699 if (fSections)
700 {
701 wzName += lstrlenW(sczCurrentSectionPrefix);
702 }
703
704 // OK, now just write the name/value pair, if it isn't deleted
705 if (pi->sczValuePrefix)
706 {
707 hr = StrAllocConcat(&sczContents, pi->sczValuePrefix, 0);
708 IniExitOnFailure(hr, "Failed to concat value prefix to ini output buffer");
709 }
710
711 hr = StrAllocConcat(&sczContents, wzName, 0);
712 IniExitOnFailure(hr, "Failed to concat value name to ini output buffer");
713
714 hr = StrAllocConcat(&sczContents, pi->sczValueSeparator, 0);
715 IniExitOnFailure(hr, "Failed to concat value separator to ini output buffer");
716
717 hr = StrAllocConcat(&sczContents, pi->rgivValues[i].wzValue, 0);
718 IniExitOnFailure(hr, "Failed to concat value to ini output buffer");
719
720 hr = StrAllocConcat(&sczContents, L"\r\n", 2);
721 IniExitOnFailure(hr, "Failed to add endline to ini output buffer in-memory");
722 }
723
724 // If no path was specified, use the path to the file we parsed
725 if (NULL == wzPath)
726 {
727 wzPath = pi->sczPath;
728 }
729
730 hr = FileFromString(wzPath, 0, sczContents, feEncoding);
731 IniExitOnFailure(hr, "Failed to write INI contents out to file: %ls", wzPath);
732
733LExit:
734 ReleaseStr(sczContents);
735 ReleaseStr(sczCurrentSectionPrefix);
736 ReleaseStr(sczNewSectionPrefix);
737
738 return hr;
739}
740
741static void UninitializeIniValue(
742 INI_VALUE *pivValue
743 )
744{
745 ReleaseStr(const_cast<LPWSTR>(pivValue->wzName));
746 ReleaseStr(const_cast<LPWSTR>(pivValue->wzValue));
747}
748
749static HRESULT GetSectionPrefixFromName(
750 __in_z LPCWSTR wzName,
751 __deref_inout_z LPWSTR* psczOutput
752 )
753{
754 HRESULT hr = S_OK;
755 LPCWSTR wzSectionDelimiter = NULL;
756
757 ReleaseNullStr(*psczOutput);
758
759 wzSectionDelimiter = wcsstr(wzName, wzSectionSeparator);
760 if (wzSectionDelimiter && wzSectionDelimiter != wzName)
761 {
762 hr = StrAllocString(psczOutput, wzName, wzSectionDelimiter - wzName + 1);
763 IniExitOnFailure(hr, "Failed to copy section prefix");
764 }
765
766LExit:
767 return hr;
768}
diff --git a/src/libs/dutil/WixToolset.DUtil/jsonutil.cpp b/src/libs/dutil/WixToolset.DUtil/jsonutil.cpp
new file mode 100644
index 00000000..3450ba59
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/jsonutil.cpp
@@ -0,0 +1,687 @@
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// Exit macros
7#define JsonExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_JSONUTIL, x, s, __VA_ARGS__)
8#define JsonExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_JSONUTIL, x, s, __VA_ARGS__)
9#define JsonExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_JSONUTIL, x, s, __VA_ARGS__)
10#define JsonExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_JSONUTIL, x, s, __VA_ARGS__)
11#define JsonExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_JSONUTIL, x, s, __VA_ARGS__)
12#define JsonExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_JSONUTIL, x, s, __VA_ARGS__)
13#define JsonExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_JSONUTIL, p, x, e, s, __VA_ARGS__)
14#define JsonExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_JSONUTIL, p, x, s, __VA_ARGS__)
15#define JsonExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_JSONUTIL, p, x, e, s, __VA_ARGS__)
16#define JsonExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_JSONUTIL, p, x, s, __VA_ARGS__)
17#define JsonExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_JSONUTIL, e, x, s, __VA_ARGS__)
18#define JsonExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_JSONUTIL, g, x, s, __VA_ARGS__)
19
20const DWORD JSON_STACK_INCREMENT = 5;
21
22// Prototypes
23static HRESULT DoStart(
24 __in JSON_WRITER* pWriter,
25 __in JSON_TOKEN tokenStart,
26 __in_z LPCWSTR wzStartString
27 );
28static HRESULT DoEnd(
29 __in JSON_WRITER* pWriter,
30 __in JSON_TOKEN tokenEnd,
31 __in_z LPCWSTR wzEndString
32 );
33static HRESULT DoKey(
34 __in JSON_WRITER* pWriter,
35 __in_z LPCWSTR wzKey
36 );
37static HRESULT DoValue(
38 __in JSON_WRITER* pWriter,
39 __in_z_opt LPCWSTR wzValue
40 );
41static HRESULT EnsureTokenStack(
42 __in JSON_WRITER* pWriter
43 );
44static HRESULT SerializeJsonString(
45 __out_z LPWSTR* psczJsonString,
46 __in_z LPCWSTR wzString
47 );
48
49
50
51DAPI_(HRESULT) JsonInitializeReader(
52 __in_z LPCWSTR wzJson,
53 __in JSON_READER* pReader
54 )
55{
56 HRESULT hr = S_OK;
57
58 memset(pReader, 0, sizeof(JSON_READER));
59 ::InitializeCriticalSection(&pReader->cs);
60
61 hr = StrAllocString(&pReader->sczJson, wzJson, 0);
62 JsonExitOnFailure(hr, "Failed to allocate json string.");
63
64 pReader->pwz = pReader->sczJson;
65
66LExit:
67 return hr;
68}
69
70
71DAPI_(void) JsonUninitializeReader(
72 __in JSON_READER* pReader
73 )
74{
75 ReleaseStr(pReader->sczJson);
76
77 ::DeleteCriticalSection(&pReader->cs);
78 memset(pReader, 0, sizeof(JSON_READER));
79}
80
81
82static HRESULT NextToken(
83 __in JSON_READER* pReader,
84 __out JSON_TOKEN* pToken
85 )
86{
87 HRESULT hr = S_OK;
88
89 // Skip whitespace.
90 while (L' ' == *pReader->pwz ||
91 L'\t' == *pReader->pwz ||
92 L'\r' == *pReader->pwz ||
93 L'\n' == *pReader->pwz ||
94 L',' == *pReader->pwz)
95 {
96 ++pReader->pwz;
97 }
98
99 switch (*pReader->pwz)
100 {
101 case L'{':
102 *pToken = JSON_TOKEN_OBJECT_START;
103 break;
104
105 case L':': // begin object value
106 *pToken = JSON_TOKEN_OBJECT_VALUE;
107 break;
108
109 case L'}': // end object
110 *pToken = JSON_TOKEN_OBJECT_END;
111 break;
112
113 case L'[': // begin array
114 *pToken = JSON_TOKEN_ARRAY_START;
115 break;
116
117 case L']': // end array
118 *pToken = JSON_TOKEN_ARRAY_END;
119 break;
120
121 case L'"': // string
122 *pToken = JSON_TOKEN_OBJECT_START == *pToken ? JSON_TOKEN_VALUE : JSON_TOKEN_OBJECT_KEY;
123 break;
124
125 case L't': // true
126 case L'f': // false
127 case L'n': // null
128 case L'-': // number
129 case L'0':
130 case L'1':
131 case L'2':
132 case L'3':
133 case L'4':
134 case L'5':
135 case L'6':
136 case L'7':
137 case L'8':
138 case L'9':
139 *pToken = JSON_TOKEN_VALUE;
140 break;
141
142 case L'\0':
143 hr = E_NOMOREITEMS;
144 break;
145
146 default:
147 hr = E_INVALIDSTATE;
148 }
149
150 return hr;
151}
152
153
154DAPI_(HRESULT) JsonReadNext(
155 __in JSON_READER* pReader,
156 __out JSON_TOKEN* pToken,
157 __out JSON_VALUE* pValue
158 )
159{
160 HRESULT hr = S_OK;
161 //WCHAR wz;
162 //JSON_TOKEN token = JSON_TOKEN_NONE;
163
164 ::EnterCriticalSection(&pReader->cs);
165
166 hr = NextToken(pReader, pToken);
167 if (E_NOMOREITEMS == hr)
168 {
169 ExitFunction();
170 }
171 JsonExitOnFailure(hr, "Failed to get next token.");
172
173 if (JSON_TOKEN_VALUE == *pToken)
174 {
175 hr = JsonReadValue(pReader, pValue);
176 }
177 else
178 {
179 ++pReader->pwz;
180 }
181
182LExit:
183 ::LeaveCriticalSection(&pReader->cs);
184 return hr;
185}
186
187
188DAPI_(HRESULT) JsonReadValue(
189 __in JSON_READER* /*pReader*/,
190 __in JSON_VALUE* /*pValue*/
191 )
192{
193 HRESULT hr = S_OK;
194
195//LExit:
196 return hr;
197}
198
199
200DAPI_(HRESULT) JsonInitializeWriter(
201 __in JSON_WRITER* pWriter
202 )
203{
204 memset(pWriter, 0, sizeof(JSON_WRITER));
205 ::InitializeCriticalSection(&pWriter->cs);
206
207 return S_OK;
208}
209
210
211DAPI_(void) JsonUninitializeWriter(
212 __in JSON_WRITER* pWriter
213 )
214{
215 ReleaseMem(pWriter->rgTokenStack);
216 ReleaseStr(pWriter->sczJson);
217
218 ::DeleteCriticalSection(&pWriter->cs);
219 memset(pWriter, 0, sizeof(JSON_WRITER));
220}
221
222
223DAPI_(HRESULT) JsonWriteBool(
224 __in JSON_WRITER* pWriter,
225 __in BOOL fValue
226 )
227{
228 HRESULT hr = S_OK;
229 LPWSTR sczValue = NULL;
230
231 hr = StrAllocString(&sczValue, fValue ? L"true" : L"false", 0);
232 JsonExitOnFailure(hr, "Failed to convert boolean to string.");
233
234 hr = DoValue(pWriter, sczValue);
235 JsonExitOnFailure(hr, "Failed to add boolean to JSON.");
236
237LExit:
238 ReleaseStr(sczValue);
239 return hr;
240}
241
242
243DAPI_(HRESULT) JsonWriteNumber(
244 __in JSON_WRITER* pWriter,
245 __in DWORD dwValue
246 )
247{
248 HRESULT hr = S_OK;
249 LPWSTR sczValue = NULL;
250
251 hr = StrAllocFormatted(&sczValue, L"%u", dwValue);
252 JsonExitOnFailure(hr, "Failed to convert number to string.");
253
254 hr = DoValue(pWriter, sczValue);
255 JsonExitOnFailure(hr, "Failed to add number to JSON.");
256
257LExit:
258 ReleaseStr(sczValue);
259 return hr;
260}
261
262
263DAPI_(HRESULT) JsonWriteString(
264 __in JSON_WRITER* pWriter,
265 __in_z LPCWSTR wzValue
266 )
267{
268 HRESULT hr = S_OK;
269 LPWSTR sczJsonString = NULL;
270
271 hr = SerializeJsonString(&sczJsonString, wzValue);
272 JsonExitOnFailure(hr, "Failed to allocate string JSON.");
273
274 hr = DoValue(pWriter, sczJsonString);
275 JsonExitOnFailure(hr, "Failed to add string to JSON.");
276
277LExit:
278 ReleaseStr(sczJsonString);
279 return hr;
280}
281
282
283DAPI_(HRESULT) JsonWriteArrayStart(
284 __in JSON_WRITER* pWriter
285 )
286{
287 HRESULT hr = S_OK;
288
289 hr = DoStart(pWriter, JSON_TOKEN_ARRAY_START, L"[");
290 JsonExitOnFailure(hr, "Failed to start JSON array.");
291
292LExit:
293 return hr;
294}
295
296
297DAPI_(HRESULT) JsonWriteArrayEnd(
298 __in JSON_WRITER* pWriter
299 )
300{
301 HRESULT hr = S_OK;
302
303 hr = DoEnd(pWriter, JSON_TOKEN_ARRAY_END, L"]");
304 JsonExitOnFailure(hr, "Failed to end JSON array.");
305
306LExit:
307 return hr;
308}
309
310
311DAPI_(HRESULT) JsonWriteObjectStart(
312 __in JSON_WRITER* pWriter
313 )
314{
315 HRESULT hr = S_OK;
316
317 hr = DoStart(pWriter, JSON_TOKEN_OBJECT_START, L"{");
318 JsonExitOnFailure(hr, "Failed to start JSON object.");
319
320LExit:
321 return hr;
322}
323
324
325DAPI_(HRESULT) JsonWriteObjectKey(
326 __in JSON_WRITER* pWriter,
327 __in_z LPCWSTR wzKey
328 )
329{
330 HRESULT hr = S_OK;
331 LPWSTR sczObjectKey = NULL;
332
333 hr = StrAllocFormatted(&sczObjectKey, L"\"%ls\":", wzKey);
334 JsonExitOnFailure(hr, "Failed to allocate JSON object key.");
335
336 hr = DoKey(pWriter, sczObjectKey);
337 JsonExitOnFailure(hr, "Failed to add object key to JSON.");
338
339LExit:
340 ReleaseStr(sczObjectKey);
341 return hr;
342}
343
344
345DAPI_(HRESULT) JsonWriteObjectEnd(
346 __in JSON_WRITER* pWriter
347 )
348{
349 HRESULT hr = S_OK;
350
351 hr = DoEnd(pWriter, JSON_TOKEN_OBJECT_END, L"}");
352 JsonExitOnFailure(hr, "Failed to end JSON object.");
353
354LExit:
355 return hr;
356}
357
358
359static HRESULT DoStart(
360 __in JSON_WRITER* pWriter,
361 __in JSON_TOKEN tokenStart,
362 __in_z LPCWSTR wzStartString
363 )
364{
365 Assert(JSON_TOKEN_ARRAY_START == tokenStart || JSON_TOKEN_OBJECT_START == tokenStart);
366
367 HRESULT hr = S_OK;
368 JSON_TOKEN token = JSON_TOKEN_NONE;
369 BOOL fNeedComma = FALSE;
370 BOOL fPushToken = TRUE;
371
372 ::EnterCriticalSection(&pWriter->cs);
373
374 hr = EnsureTokenStack(pWriter);
375 JsonExitOnFailure(hr, "Failed to ensure token stack for start.");
376
377 token = pWriter->rgTokenStack[pWriter->cTokens - 1];
378 switch (token)
379 {
380 case JSON_TOKEN_NONE:
381 token = tokenStart;
382 fPushToken = FALSE;
383 break;
384
385 case JSON_TOKEN_ARRAY_START: // array start changes to array value.
386 token = JSON_TOKEN_ARRAY_VALUE;
387 break;
388
389 case JSON_TOKEN_ARRAY_VALUE:
390 case JSON_TOKEN_ARRAY_END:
391 case JSON_TOKEN_OBJECT_END:
392 fNeedComma = TRUE;
393 break;
394
395 default: // everything else is not allowed.
396 hr = E_UNEXPECTED;
397 break;
398 }
399 JsonExitOnRootFailure(hr, "Cannot start array or object to JSON serializer now.");
400
401 if (fNeedComma)
402 {
403 hr = StrAllocConcat(&pWriter->sczJson, L",", 0);
404 JsonExitOnFailure(hr, "Failed to add comma for start array or object to JSON.");
405 }
406
407 hr = StrAllocConcat(&pWriter->sczJson, wzStartString, 0);
408 JsonExitOnFailure(hr, "Failed to start JSON array or object.");
409
410 pWriter->rgTokenStack[pWriter->cTokens - 1] = token;
411 if (fPushToken)
412 {
413 pWriter->rgTokenStack[pWriter->cTokens] = tokenStart;
414 ++pWriter->cTokens;
415 }
416
417LExit:
418 ::LeaveCriticalSection(&pWriter->cs);
419 return hr;
420}
421
422
423static HRESULT DoEnd(
424 __in JSON_WRITER* pWriter,
425 __in JSON_TOKEN tokenEnd,
426 __in_z LPCWSTR wzEndString
427 )
428{
429 HRESULT hr = S_OK;
430
431 ::EnterCriticalSection(&pWriter->cs);
432
433 if (!pWriter->rgTokenStack || 0 == pWriter->cTokens)
434 {
435 hr = E_UNEXPECTED;
436 JsonExitOnRootFailure(hr, "Failure to pop token because the stack is empty.");
437 }
438 else
439 {
440 JSON_TOKEN token = pWriter->rgTokenStack[pWriter->cTokens - 1];
441 if ((JSON_TOKEN_ARRAY_END == tokenEnd && JSON_TOKEN_ARRAY_START != token && JSON_TOKEN_ARRAY_VALUE != token) ||
442 (JSON_TOKEN_OBJECT_END == tokenEnd && JSON_TOKEN_OBJECT_START != token && JSON_TOKEN_OBJECT_VALUE != token))
443 {
444 hr = E_UNEXPECTED;
445 JsonExitOnRootFailure(hr, "Failure to pop token because the stack did not match the expected token: %d", tokenEnd);
446 }
447 }
448
449 hr = StrAllocConcat(&pWriter->sczJson, wzEndString, 0);
450 JsonExitOnFailure(hr, "Failed to end JSON array or object.");
451
452 --pWriter->cTokens;
453
454LExit:
455 ::LeaveCriticalSection(&pWriter->cs);
456 return hr;
457}
458
459
460static HRESULT DoKey(
461 __in JSON_WRITER* pWriter,
462 __in_z LPCWSTR wzKey
463 )
464{
465 HRESULT hr = S_OK;
466 JSON_TOKEN token = JSON_TOKEN_NONE;
467 BOOL fNeedComma = FALSE;
468
469 ::EnterCriticalSection(&pWriter->cs);
470
471 hr = EnsureTokenStack(pWriter);
472 JsonExitOnFailure(hr, "Failed to ensure token stack for key.");
473
474 token = pWriter->rgTokenStack[pWriter->cTokens - 1];
475 switch (token)
476 {
477 case JSON_TOKEN_OBJECT_START:
478 token = JSON_TOKEN_OBJECT_KEY;
479 break;
480
481 case JSON_TOKEN_OBJECT_VALUE:
482 token = JSON_TOKEN_OBJECT_KEY;
483 fNeedComma = TRUE;
484 break;
485
486 default: // everything else is not allowed.
487 hr = E_UNEXPECTED;
488 break;
489 }
490 JsonExitOnRootFailure(hr, "Cannot add key to JSON serializer now.");
491
492 if (fNeedComma)
493 {
494 hr = StrAllocConcat(&pWriter->sczJson, L",", 0);
495 JsonExitOnFailure(hr, "Failed to add comma for key to JSON.");
496 }
497
498 hr = StrAllocConcat(&pWriter->sczJson, wzKey, 0);
499 JsonExitOnFailure(hr, "Failed to add key to JSON.");
500
501 pWriter->rgTokenStack[pWriter->cTokens - 1] = token;
502
503LExit:
504 ::LeaveCriticalSection(&pWriter->cs);
505 return hr;
506}
507
508
509static HRESULT DoValue(
510 __in JSON_WRITER* pWriter,
511 __in_z_opt LPCWSTR wzValue
512 )
513{
514 HRESULT hr = S_OK;
515 JSON_TOKEN token = JSON_TOKEN_NONE;
516 BOOL fNeedComma = FALSE;
517
518 ::EnterCriticalSection(&pWriter->cs);
519
520 hr = EnsureTokenStack(pWriter);
521 JsonExitOnFailure(hr, "Failed to ensure token stack for value.");
522
523 token = pWriter->rgTokenStack[pWriter->cTokens - 1];
524 switch (token)
525 {
526 case JSON_TOKEN_ARRAY_START:
527 token = JSON_TOKEN_ARRAY_VALUE;
528 break;
529
530 case JSON_TOKEN_ARRAY_VALUE:
531 fNeedComma = TRUE;
532 break;
533
534 case JSON_TOKEN_OBJECT_KEY:
535 token = JSON_TOKEN_OBJECT_VALUE;
536 break;
537
538 case JSON_TOKEN_NONE:
539 token = JSON_TOKEN_VALUE;
540 break;
541
542 default: // everything else is not allowed.
543 hr = E_UNEXPECTED;
544 break;
545 }
546 JsonExitOnRootFailure(hr, "Cannot add value to JSON serializer now.");
547
548 if (fNeedComma)
549 {
550 hr = StrAllocConcat(&pWriter->sczJson, L",", 0);
551 JsonExitOnFailure(hr, "Failed to add comma for value to JSON.");
552 }
553
554 if (wzValue)
555 {
556 hr = StrAllocConcat(&pWriter->sczJson, wzValue, 0);
557 JsonExitOnFailure(hr, "Failed to add value to JSON.");
558 }
559 else
560 {
561 hr = StrAllocConcat(&pWriter->sczJson, L"null", 0);
562 JsonExitOnFailure(hr, "Failed to add null value to JSON.");
563 }
564
565 pWriter->rgTokenStack[pWriter->cTokens - 1] = token;
566
567LExit:
568 ::LeaveCriticalSection(&pWriter->cs);
569 return hr;
570}
571
572
573static HRESULT EnsureTokenStack(
574 __in JSON_WRITER* pWriter
575 )
576{
577 HRESULT hr = S_OK;
578 DWORD cNumAlloc = pWriter->cTokens != 0 ? pWriter->cTokens : 0;
579
580 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pWriter->rgTokenStack), cNumAlloc, sizeof(JSON_TOKEN), JSON_STACK_INCREMENT);
581 JsonExitOnFailure(hr, "Failed to allocate JSON token stack.");
582
583 if (0 == pWriter->cTokens)
584 {
585 pWriter->rgTokenStack[0] = JSON_TOKEN_NONE;
586 ++pWriter->cTokens;
587 }
588
589LExit:
590 return hr;
591}
592
593
594static HRESULT SerializeJsonString(
595 __out_z LPWSTR* psczJsonString,
596 __in_z LPCWSTR wzString
597 )
598{
599 HRESULT hr = S_OK;
600 DWORD cchRequired = 3; // start with enough space for null terminated empty quoted string (aka: ""\0)
601
602 for (LPCWSTR pch = wzString; *pch; ++pch)
603 {
604 // If it is a special JSON character, add space for the escape backslash.
605 if (L'"' == *pch || L'\\' == *pch || L'/' == *pch || L'\b' == *pch || L'\f' == *pch || L'\n' == *pch || L'\r' == *pch || L'\t' == *pch)
606 {
607 ++cchRequired;
608 }
609
610 ++cchRequired;
611 }
612
613 hr = StrAlloc(psczJsonString, cchRequired);
614 JsonExitOnFailure(hr, "Failed to allocate space for JSON string.");
615
616 LPWSTR pchTarget = *psczJsonString;
617
618 *pchTarget = L'\"';
619 ++pchTarget;
620
621 for (LPCWSTR pch = wzString; *pch; ++pch, ++pchTarget)
622 {
623 // If it is a special JSON character, handle it or just add the character as is.
624 switch (*pch)
625 {
626 case L'"':
627 *pchTarget = L'\\';
628 ++pchTarget;
629 *pchTarget = L'"';
630 break;
631
632 case L'\\':
633 *pchTarget = L'\\';
634 ++pchTarget;
635 *pchTarget = L'\\';
636 break;
637
638 case L'/':
639 *pchTarget = L'\\';
640 ++pchTarget;
641 *pchTarget = L'/';
642 break;
643
644 case L'\b':
645 *pchTarget = L'\\';
646 ++pchTarget;
647 *pchTarget = L'b';
648 break;
649
650 case L'\f':
651 *pchTarget = L'\\';
652 ++pchTarget;
653 *pchTarget = L'f';
654 break;
655
656 case L'\n':
657 *pchTarget = L'\\';
658 ++pchTarget;
659 *pchTarget = L'n';
660 break;
661
662 case L'\r':
663 *pchTarget = L'\\';
664 ++pchTarget;
665 *pchTarget = L'r';
666 break;
667
668 case L'\t':
669 *pchTarget = L'\\';
670 ++pchTarget;
671 *pchTarget = L't';
672 break;
673
674 default:
675 *pchTarget = *pch;
676 break;
677 }
678
679 }
680
681 *pchTarget = L'\"';
682 ++pchTarget;
683 *pchTarget = L'\0';
684
685LExit:
686 return hr;
687}
diff --git a/src/libs/dutil/WixToolset.DUtil/locutil.cpp b/src/libs/dutil/WixToolset.DUtil/locutil.cpp
new file mode 100644
index 00000000..c4567c03
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/locutil.cpp
@@ -0,0 +1,628 @@
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// Exit macros
7#define LocExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_LOCUTIL, x, s, __VA_ARGS__)
8#define LocExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_LOCUTIL, x, s, __VA_ARGS__)
9#define LocExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_LOCUTIL, x, s, __VA_ARGS__)
10#define LocExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_LOCUTIL, x, s, __VA_ARGS__)
11#define LocExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_LOCUTIL, x, s, __VA_ARGS__)
12#define LocExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_LOCUTIL, x, s, __VA_ARGS__)
13#define LocExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_LOCUTIL, p, x, e, s, __VA_ARGS__)
14#define LocExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_LOCUTIL, p, x, s, __VA_ARGS__)
15#define LocExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_LOCUTIL, p, x, e, s, __VA_ARGS__)
16#define LocExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_LOCUTIL, p, x, s, __VA_ARGS__)
17#define LocExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_LOCUTIL, e, x, s, __VA_ARGS__)
18#define LocExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_LOCUTIL, g, x, s, __VA_ARGS__)
19
20// prototypes
21static HRESULT ParseWxl(
22 __in IXMLDOMDocument* pixd,
23 __out WIX_LOCALIZATION** ppWixLoc
24 );
25static HRESULT ParseWxlStrings(
26 __in IXMLDOMElement* pElement,
27 __in WIX_LOCALIZATION* pWixLoc
28 );
29static HRESULT ParseWxlControls(
30 __in IXMLDOMElement* pElement,
31 __in WIX_LOCALIZATION* pWixLoc
32 );
33static HRESULT ParseWxlString(
34 __in IXMLDOMNode* pixn,
35 __in DWORD dwIdx,
36 __in WIX_LOCALIZATION* pWixLoc
37 );
38static HRESULT ParseWxlControl(
39 __in IXMLDOMNode* pixn,
40 __in DWORD dwIdx,
41 __in WIX_LOCALIZATION* pWixLoc
42 );
43
44// from Winnls.h
45#ifndef MUI_LANGUAGE_ID
46#define MUI_LANGUAGE_ID 0x4 // Use traditional language ID convention
47#endif
48#ifndef MUI_MERGE_USER_FALLBACK
49#define MUI_MERGE_USER_FALLBACK 0x20 // GetThreadPreferredUILanguages merges in user preferred languages
50#endif
51#ifndef MUI_MERGE_SYSTEM_FALLBACK
52#define MUI_MERGE_SYSTEM_FALLBACK 0x10 // GetThreadPreferredUILanguages merges in parent and base languages
53#endif
54typedef WINBASEAPI BOOL (WINAPI *GET_THREAD_PREFERRED_UI_LANGUAGES) (
55 __in DWORD dwFlags,
56 __out PULONG pulNumLanguages,
57 __out_ecount_opt(*pcchLanguagesBuffer) PZZWSTR pwszLanguagesBuffer,
58 __inout PULONG pcchLanguagesBuffer
59);
60
61extern "C" HRESULT DAPI LocProbeForFile(
62 __in_z LPCWSTR wzBasePath,
63 __in_z LPCWSTR wzLocFileName,
64 __in_z_opt LPCWSTR wzLanguage,
65 __inout LPWSTR* psczPath
66 )
67{
68 HRESULT hr = S_OK;
69 LPWSTR sczProbePath = NULL;
70 LANGID langid = 0;
71 LPWSTR sczLangIdFile = NULL;
72 LPWSTR sczLangsBuff = NULL;
73 GET_THREAD_PREFERRED_UI_LANGUAGES pvfnGetThreadPreferredUILanguages =
74 reinterpret_cast<GET_THREAD_PREFERRED_UI_LANGUAGES>(
75 GetProcAddress(GetModuleHandle("Kernel32.dll"), "GetThreadPreferredUILanguages"));
76
77 // If a language was specified, look for a loc file in that as a directory.
78 if (wzLanguage && *wzLanguage)
79 {
80 hr = PathConcat(wzBasePath, wzLanguage, &sczProbePath);
81 LocExitOnFailure(hr, "Failed to concat base path to language.");
82
83 hr = PathConcat(sczProbePath, wzLocFileName, &sczProbePath);
84 LocExitOnFailure(hr, "Failed to concat loc file name to probe path.");
85
86 if (FileExistsEx(sczProbePath, NULL))
87 {
88 ExitFunction();
89 }
90 }
91
92 if (pvfnGetThreadPreferredUILanguages)
93 {
94 ULONG nLangs;
95 ULONG cchLangs = 0;
96 DWORD dwFlags = MUI_LANGUAGE_ID | MUI_MERGE_USER_FALLBACK | MUI_MERGE_SYSTEM_FALLBACK;
97 if (!(*pvfnGetThreadPreferredUILanguages)(dwFlags, &nLangs, NULL, &cchLangs))
98 {
99 LocExitWithLastError(hr, "GetThreadPreferredUILanguages failed to return buffer size.");
100 }
101
102 hr = StrAlloc(&sczLangsBuff, cchLangs);
103 LocExitOnFailure(hr, "Failed to allocate buffer for languages");
104
105 nLangs = 0;
106 if (!(*pvfnGetThreadPreferredUILanguages)(dwFlags, &nLangs, sczLangsBuff, &cchLangs))
107 {
108 LocExitWithLastError(hr, "GetThreadPreferredUILanguages failed to return language list.");
109 }
110
111 LPWSTR szLangs = sczLangsBuff;
112 for (ULONG i = 0; i < nLangs; ++i, szLangs += 5)
113 {
114 // StrHexDecode assumes low byte is first. We'll need to swap the bytes once we parse out the value.
115 hr = StrHexDecode(szLangs, reinterpret_cast<BYTE*>(&langid), sizeof(langid));
116 LocExitOnFailure(hr, "Failed to parse langId.");
117
118 langid = MAKEWORD(HIBYTE(langid), LOBYTE(langid));
119 hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName);
120 LocExitOnFailure(hr, "Failed to format user preferred langid.");
121
122 hr = PathConcat(wzBasePath, sczLangIdFile, &sczProbePath);
123 LocExitOnFailure(hr, "Failed to concat user preferred langid file name to base path.");
124
125 if (FileExistsEx(sczProbePath, NULL))
126 {
127 ExitFunction();
128 }
129 }
130 }
131
132 langid = ::GetUserDefaultUILanguage();
133
134 hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName);
135 LocExitOnFailure(hr, "Failed to format user langid.");
136
137 hr = PathConcat(wzBasePath, sczLangIdFile, &sczProbePath);
138 LocExitOnFailure(hr, "Failed to concat user langid file name to base path.");
139
140 if (FileExistsEx(sczProbePath, NULL))
141 {
142 ExitFunction();
143 }
144
145 if (MAKELANGID(langid & 0x3FF, SUBLANG_DEFAULT) != langid)
146 {
147 langid = MAKELANGID(langid & 0x3FF, SUBLANG_DEFAULT);
148
149 hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName);
150 LocExitOnFailure(hr, "Failed to format user langid (default sublang).");
151
152 hr = PathConcat(wzBasePath, sczLangIdFile, &sczProbePath);
153 LocExitOnFailure(hr, "Failed to concat user langid file name to base path (default sublang).");
154
155 if (FileExistsEx(sczProbePath, NULL))
156 {
157 ExitFunction();
158 }
159 }
160
161 langid = ::GetSystemDefaultUILanguage();
162
163 hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName);
164 LocExitOnFailure(hr, "Failed to format system langid.");
165
166 hr = PathConcat(wzBasePath, sczLangIdFile, &sczProbePath);
167 LocExitOnFailure(hr, "Failed to concat system langid file name to base path.");
168
169 if (FileExistsEx(sczProbePath, NULL))
170 {
171 ExitFunction();
172 }
173
174 if (MAKELANGID(langid & 0x3FF, SUBLANG_DEFAULT) != langid)
175 {
176 langid = MAKELANGID(langid & 0x3FF, SUBLANG_DEFAULT);
177
178 hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName);
179 LocExitOnFailure(hr, "Failed to format user langid (default sublang).");
180
181 hr = PathConcat(wzBasePath, sczLangIdFile, &sczProbePath);
182 LocExitOnFailure(hr, "Failed to concat user langid file name to base path (default sublang).");
183
184 if (FileExistsEx(sczProbePath, NULL))
185 {
186 ExitFunction();
187 }
188 }
189
190 // Finally, look for the loc file in the base path.
191 hr = PathConcat(wzBasePath, wzLocFileName, &sczProbePath);
192 LocExitOnFailure(hr, "Failed to concat loc file name to base path.");
193
194 if (!FileExistsEx(sczProbePath, NULL))
195 {
196 hr = E_FILENOTFOUND;
197 }
198
199LExit:
200 if (SUCCEEDED(hr))
201 {
202 hr = StrAllocString(psczPath, sczProbePath, 0);
203 }
204
205 ReleaseStr(sczLangIdFile);
206 ReleaseStr(sczProbePath);
207 ReleaseStr(sczLangsBuff);
208
209 return hr;
210}
211
212extern "C" HRESULT DAPI LocLoadFromFile(
213 __in_z LPCWSTR wzWxlFile,
214 __out WIX_LOCALIZATION** ppWixLoc
215 )
216{
217 HRESULT hr = S_OK;
218 IXMLDOMDocument* pixd = NULL;
219
220 hr = XmlLoadDocumentFromFile(wzWxlFile, &pixd);
221 LocExitOnFailure(hr, "Failed to load WXL file as XML document.");
222
223 hr = ParseWxl(pixd, ppWixLoc);
224 LocExitOnFailure(hr, "Failed to parse WXL.");
225
226LExit:
227 ReleaseObject(pixd);
228
229 return hr;
230}
231
232extern "C" HRESULT DAPI LocLoadFromResource(
233 __in HMODULE hModule,
234 __in_z LPCSTR szResource,
235 __out WIX_LOCALIZATION** ppWixLoc
236 )
237{
238 HRESULT hr = S_OK;
239 LPVOID pvResource = NULL;
240 DWORD cbResource = 0;
241 LPWSTR sczXml = NULL;
242 IXMLDOMDocument* pixd = NULL;
243
244 hr = ResReadData(hModule, szResource, &pvResource, &cbResource);
245 LocExitOnFailure(hr, "Failed to read theme from resource.");
246
247 hr = StrAllocStringAnsi(&sczXml, reinterpret_cast<LPCSTR>(pvResource), cbResource, CP_UTF8);
248 LocExitOnFailure(hr, "Failed to convert XML document data from UTF-8 to unicode string.");
249
250 hr = XmlLoadDocument(sczXml, &pixd);
251 LocExitOnFailure(hr, "Failed to load theme resource as XML document.");
252
253 hr = ParseWxl(pixd, ppWixLoc);
254 LocExitOnFailure(hr, "Failed to parse WXL.");
255
256LExit:
257 ReleaseObject(pixd);
258 ReleaseStr(sczXml);
259
260 return hr;
261}
262
263extern "C" void DAPI LocFree(
264 __in_opt WIX_LOCALIZATION* pWixLoc
265 )
266{
267 if (pWixLoc)
268 {
269 for (DWORD idx = 0; idx < pWixLoc->cLocStrings; ++idx)
270 {
271 ReleaseStr(pWixLoc->rgLocStrings[idx].wzId);
272 ReleaseStr(pWixLoc->rgLocStrings[idx].wzText);
273 }
274
275 for (DWORD idx = 0; idx < pWixLoc->cLocControls; ++idx)
276 {
277 ReleaseStr(pWixLoc->rgLocControls[idx].wzControl);
278 ReleaseStr(pWixLoc->rgLocControls[idx].wzText);
279 }
280
281 ReleaseMem(pWixLoc->rgLocStrings);
282 ReleaseMem(pWixLoc->rgLocControls);
283 ReleaseMem(pWixLoc);
284 }
285}
286
287extern "C" HRESULT DAPI LocLocalizeString(
288 __in const WIX_LOCALIZATION* pWixLoc,
289 __inout LPWSTR* ppsczInput
290 )
291{
292 Assert(ppsczInput && pWixLoc);
293 HRESULT hr = S_OK;
294
295 for (DWORD i = 0; i < pWixLoc->cLocStrings; ++i)
296 {
297 hr = StrReplaceStringAll(ppsczInput, pWixLoc->rgLocStrings[i].wzId, pWixLoc->rgLocStrings[i].wzText);
298 LocExitOnFailure(hr, "Localizing string failed.");
299 }
300
301LExit:
302 return hr;
303}
304
305extern "C" HRESULT DAPI LocGetControl(
306 __in const WIX_LOCALIZATION* pWixLoc,
307 __in_z LPCWSTR wzId,
308 __out LOC_CONTROL** ppLocControl
309 )
310{
311 HRESULT hr = S_OK;
312 LOC_CONTROL* pLocControl = NULL;
313
314 for (DWORD i = 0; i < pWixLoc->cLocControls; ++i)
315 {
316 pLocControl = &pWixLoc->rgLocControls[i];
317
318 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pLocControl->wzControl, -1, wzId, -1))
319 {
320 *ppLocControl = pLocControl;
321 ExitFunction1(hr = S_OK);
322 }
323 }
324
325 hr = E_NOTFOUND;
326
327LExit:
328 return hr;
329}
330
331extern "C" HRESULT DAPI LocGetString(
332 __in const WIX_LOCALIZATION* pWixLoc,
333 __in_z LPCWSTR wzId,
334 __out LOC_STRING** ppLocString
335 )
336{
337 HRESULT hr = E_NOTFOUND;
338 LOC_STRING* pLocString = NULL;
339
340 for (DWORD i = 0; i < pWixLoc->cLocStrings; ++i)
341 {
342 pLocString = pWixLoc->rgLocStrings + i;
343
344 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pLocString->wzId, -1, wzId, -1))
345 {
346 *ppLocString = pLocString;
347 hr = S_OK;
348 break;
349 }
350 }
351
352 return hr;
353}
354
355extern "C" HRESULT DAPI LocAddString(
356 __in WIX_LOCALIZATION* pWixLoc,
357 __in_z LPCWSTR wzId,
358 __in_z LPCWSTR wzLocString,
359 __in BOOL bOverridable
360 )
361{
362 HRESULT hr = S_OK;
363
364 ++pWixLoc->cLocStrings;
365 pWixLoc->rgLocStrings = static_cast<LOC_STRING*>(MemReAlloc(pWixLoc->rgLocStrings, sizeof(LOC_STRING) * pWixLoc->cLocStrings, TRUE));
366 LocExitOnNull(pWixLoc->rgLocStrings, hr, E_OUTOFMEMORY, "Failed to reallocate memory for localization strings.");
367
368 LOC_STRING* pLocString = pWixLoc->rgLocStrings + (pWixLoc->cLocStrings - 1);
369
370 hr = StrAllocFormatted(&pLocString->wzId, L"#(loc.%s)", wzId);
371 LocExitOnFailure(hr, "Failed to set localization string Id.");
372
373 hr = StrAllocString(&pLocString->wzText, wzLocString, 0);
374 LocExitOnFailure(hr, "Failed to set localization string Text.");
375
376 pLocString->bOverridable = bOverridable;
377
378LExit:
379 return hr;
380}
381
382// helper functions
383
384static HRESULT ParseWxl(
385 __in IXMLDOMDocument* pixd,
386 __out WIX_LOCALIZATION** ppWixLoc
387 )
388{
389 HRESULT hr = S_OK;
390 IXMLDOMElement *pWxlElement = NULL;
391 WIX_LOCALIZATION* pWixLoc = NULL;
392
393 pWixLoc = static_cast<WIX_LOCALIZATION*>(MemAlloc(sizeof(WIX_LOCALIZATION), TRUE));
394 LocExitOnNull(pWixLoc, hr, E_OUTOFMEMORY, "Failed to allocate memory for Wxl file.");
395
396 // read the WixLocalization tag
397 hr = pixd->get_documentElement(&pWxlElement);
398 LocExitOnFailure(hr, "Failed to get localization element.");
399
400 // get the Language attribute if present
401 pWixLoc->dwLangId = WIX_LOCALIZATION_LANGUAGE_NOT_SET;
402 hr = XmlGetAttributeNumber(pWxlElement, L"Language", &pWixLoc->dwLangId);
403 if (S_FALSE == hr)
404 {
405 hr = S_OK;
406 }
407 LocExitOnFailure(hr, "Failed to get Language value.");
408
409 // store the strings and controls in a node list
410 hr = ParseWxlStrings(pWxlElement, pWixLoc);
411 LocExitOnFailure(hr, "Parsing localization strings failed.");
412
413 hr = ParseWxlControls(pWxlElement, pWixLoc);
414 LocExitOnFailure(hr, "Parsing localization controls failed.");
415
416 *ppWixLoc = pWixLoc;
417 pWixLoc = NULL;
418
419LExit:
420 ReleaseObject(pWxlElement);
421 ReleaseMem(pWixLoc);
422
423 return hr;
424}
425
426
427static HRESULT ParseWxlStrings(
428 __in IXMLDOMElement* pElement,
429 __in WIX_LOCALIZATION* pWixLoc
430 )
431{
432 HRESULT hr = S_OK;
433 IXMLDOMNode* pixn = NULL;
434 IXMLDOMNodeList* pixnl = NULL;
435 DWORD dwIdx = 0;
436
437 hr = XmlSelectNodes(pElement, L"String", &pixnl);
438 LocExitOnLastError(hr, "Failed to get String child nodes of Wxl File.");
439
440 hr = pixnl->get_length(reinterpret_cast<long*>(&pWixLoc->cLocStrings));
441 LocExitOnLastError(hr, "Failed to get number of String child nodes in Wxl File.");
442
443 if (0 < pWixLoc->cLocStrings)
444 {
445 pWixLoc->rgLocStrings = static_cast<LOC_STRING*>(MemAlloc(sizeof(LOC_STRING) * pWixLoc->cLocStrings, TRUE));
446 LocExitOnNull(pWixLoc->rgLocStrings, hr, E_OUTOFMEMORY, "Failed to allocate memory for localization strings.");
447
448 while (S_OK == (hr = XmlNextElement(pixnl, &pixn, NULL)))
449 {
450 hr = ParseWxlString(pixn, dwIdx, pWixLoc);
451 LocExitOnFailure(hr, "Failed to parse localization string.");
452
453 ++dwIdx;
454 ReleaseNullObject(pixn);
455 }
456
457 hr = S_OK;
458 LocExitOnFailure(hr, "Failed to enumerate all localization strings.");
459 }
460
461LExit:
462 if (FAILED(hr) && pWixLoc->rgLocStrings)
463 {
464 for (DWORD idx = 0; idx < pWixLoc->cLocStrings; ++idx)
465 {
466 ReleaseStr(pWixLoc->rgLocStrings[idx].wzId);
467 ReleaseStr(pWixLoc->rgLocStrings[idx].wzText);
468 }
469
470 ReleaseMem(pWixLoc->rgLocStrings);
471 }
472
473 ReleaseObject(pixn);
474 ReleaseObject(pixnl);
475
476 return hr;
477}
478
479static HRESULT ParseWxlControls(
480 __in IXMLDOMElement* pElement,
481 __in WIX_LOCALIZATION* pWixLoc
482 )
483{
484 HRESULT hr = S_OK;
485 IXMLDOMNode* pixn = NULL;
486 IXMLDOMNodeList* pixnl = NULL;
487 DWORD dwIdx = 0;
488
489 hr = XmlSelectNodes(pElement, L"UI|Control", &pixnl);
490 LocExitOnLastError(hr, "Failed to get UI child nodes of Wxl File.");
491
492 hr = pixnl->get_length(reinterpret_cast<long*>(&pWixLoc->cLocControls));
493 LocExitOnLastError(hr, "Failed to get number of UI child nodes in Wxl File.");
494
495 if (0 < pWixLoc->cLocControls)
496 {
497 pWixLoc->rgLocControls = static_cast<LOC_CONTROL*>(MemAlloc(sizeof(LOC_CONTROL) * pWixLoc->cLocControls, TRUE));
498 LocExitOnNull(pWixLoc->rgLocControls, hr, E_OUTOFMEMORY, "Failed to allocate memory for localized controls.");
499
500 while (S_OK == (hr = XmlNextElement(pixnl, &pixn, NULL)))
501 {
502 hr = ParseWxlControl(pixn, dwIdx, pWixLoc);
503 LocExitOnFailure(hr, "Failed to parse localized control.");
504
505 ++dwIdx;
506 ReleaseNullObject(pixn);
507 }
508
509 hr = S_OK;
510 LocExitOnFailure(hr, "Failed to enumerate all localized controls.");
511 }
512
513LExit:
514 if (FAILED(hr) && pWixLoc->rgLocControls)
515 {
516 for (DWORD idx = 0; idx < pWixLoc->cLocControls; ++idx)
517 {
518 ReleaseStr(pWixLoc->rgLocControls[idx].wzControl);
519 ReleaseStr(pWixLoc->rgLocControls[idx].wzText);
520 }
521
522 ReleaseMem(pWixLoc->rgLocControls);
523 }
524
525 ReleaseObject(pixn);
526 ReleaseObject(pixnl);
527
528 return hr;
529}
530
531static HRESULT ParseWxlString(
532 __in IXMLDOMNode* pixn,
533 __in DWORD dwIdx,
534 __in WIX_LOCALIZATION* pWixLoc
535 )
536{
537 HRESULT hr = S_OK;
538 LOC_STRING* pLocString = NULL;
539 BSTR bstrText = NULL;
540
541 pLocString = pWixLoc->rgLocStrings + dwIdx;
542
543 // Id
544 hr = XmlGetAttribute(pixn, L"Id", &bstrText);
545 LocExitOnFailure(hr, "Failed to get Xml attribute Id in Wxl file.");
546
547 hr = StrAllocFormatted(&pLocString->wzId, L"#(loc.%s)", bstrText);
548 LocExitOnFailure(hr, "Failed to duplicate Xml attribute Id in Wxl file.");
549
550 ReleaseNullBSTR(bstrText);
551
552 // Overrideable
553 hr = XmlGetAttribute(pixn, L"Overridable", &bstrText);
554 LocExitOnFailure(hr, "Failed to get Xml attribute Overridable.");
555
556 if (S_OK == hr)
557 {
558 pLocString->bOverridable = CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrText, -1, L"yes", -1);
559 }
560
561 ReleaseNullBSTR(bstrText);
562
563 // Text
564 hr = XmlGetText(pixn, &bstrText);
565 LocExitOnFailure(hr, "Failed to get Xml text in Wxl file.");
566
567 hr = StrAllocString(&pLocString->wzText, bstrText, 0);
568 LocExitOnFailure(hr, "Failed to duplicate Xml text in Wxl file.");
569
570LExit:
571 ReleaseBSTR(bstrText);
572
573 return hr;
574}
575
576static HRESULT ParseWxlControl(
577 __in IXMLDOMNode* pixn,
578 __in DWORD dwIdx,
579 __in WIX_LOCALIZATION* pWixLoc
580 )
581{
582 HRESULT hr = S_OK;
583 LOC_CONTROL* pLocControl = NULL;
584 BSTR bstrText = NULL;
585
586 pLocControl = pWixLoc->rgLocControls + dwIdx;
587
588 // Id
589 hr = XmlGetAttribute(pixn, L"Control", &bstrText);
590 LocExitOnFailure(hr, "Failed to get Xml attribute Control in Wxl file.");
591
592 hr = StrAllocString(&pLocControl->wzControl, bstrText, 0);
593 LocExitOnFailure(hr, "Failed to duplicate Xml attribute Control in Wxl file.");
594
595 ReleaseNullBSTR(bstrText);
596
597 // X
598 pLocControl->nX = LOC_CONTROL_NOT_SET;
599 hr = XmlGetAttributeNumber(pixn, L"X", reinterpret_cast<DWORD*>(&pLocControl->nX));
600 LocExitOnFailure(hr, "Failed to get control X attribute.");
601
602 // Y
603 pLocControl->nY = LOC_CONTROL_NOT_SET;
604 hr = XmlGetAttributeNumber(pixn, L"Y", reinterpret_cast<DWORD*>(&pLocControl->nY));
605 LocExitOnFailure(hr, "Failed to get control Y attribute.");
606
607 // Width
608 pLocControl->nWidth = LOC_CONTROL_NOT_SET;
609 hr = XmlGetAttributeNumber(pixn, L"Width", reinterpret_cast<DWORD*>(&pLocControl->nWidth));
610 LocExitOnFailure(hr, "Failed to get control width attribute.");
611
612 // Height
613 pLocControl->nHeight = LOC_CONTROL_NOT_SET;
614 hr = XmlGetAttributeNumber(pixn, L"Height", reinterpret_cast<DWORD*>(&pLocControl->nHeight));
615 LocExitOnFailure(hr, "Failed to get control height attribute.");
616
617 // Text
618 hr = XmlGetText(pixn, &bstrText);
619 LocExitOnFailure(hr, "Failed to get control text in Wxl file.");
620
621 hr = StrAllocString(&pLocControl->wzText, bstrText, 0);
622 LocExitOnFailure(hr, "Failed to duplicate control text in Wxl file.");
623
624LExit:
625 ReleaseBSTR(bstrText);
626
627 return hr;
628}
diff --git a/src/libs/dutil/WixToolset.DUtil/logutil.cpp b/src/libs/dutil/WixToolset.DUtil/logutil.cpp
new file mode 100644
index 00000000..ac68036a
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/logutil.cpp
@@ -0,0 +1,961 @@
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// Exit macros
7#define LoguExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_LOGUTIL, x, s, __VA_ARGS__)
8#define LoguExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_LOGUTIL, x, s, __VA_ARGS__)
9#define LoguExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_LOGUTIL, x, s, __VA_ARGS__)
10#define LoguExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_LOGUTIL, x, s, __VA_ARGS__)
11#define LoguExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_LOGUTIL, x, s, __VA_ARGS__)
12#define LoguExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_LOGUTIL, x, s, __VA_ARGS__)
13#define LoguExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_LOGUTIL, p, x, e, s, __VA_ARGS__)
14#define LoguExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_LOGUTIL, p, x, s, __VA_ARGS__)
15#define LoguExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_LOGUTIL, p, x, e, s, __VA_ARGS__)
16#define LoguExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_LOGUTIL, p, x, s, __VA_ARGS__)
17#define LoguExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_LOGUTIL, e, x, s, __VA_ARGS__)
18#define LoguExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_LOGUTIL, g, x, s, __VA_ARGS__)
19
20// globals
21static HMODULE LogUtil_hModule = NULL;
22static BOOL LogUtil_fDisabled = FALSE;
23static HANDLE LogUtil_hLog = INVALID_HANDLE_VALUE;
24static LPWSTR LogUtil_sczLogPath = NULL;
25static LPSTR LogUtil_sczPreInitBuffer = NULL;
26static REPORT_LEVEL LogUtil_rlCurrent = REPORT_STANDARD;
27static CRITICAL_SECTION LogUtil_csLog = { };
28static BOOL LogUtil_fInitializedCriticalSection = FALSE;
29
30// Customization of certain parts of the string, within a line
31static LPWSTR LogUtil_sczSpecialBeginLine = NULL;
32static LPWSTR LogUtil_sczSpecialEndLine = NULL;
33static LPWSTR LogUtil_sczSpecialAfterTimeStamp = NULL;
34
35static LPCSTR LOGUTIL_UNKNOWN = "unknown";
36static LPCSTR LOGUTIL_WARNING = "warning";
37static LPCSTR LOGUTIL_STANDARD = "standard";
38static LPCSTR LOGUTIL_VERBOSE = "verbose";
39static LPCSTR LOGUTIL_DEBUG = "debug";
40static LPCSTR LOGUTIL_NONE = "none";
41
42// prototypes
43static HRESULT LogIdWork(
44 __in REPORT_LEVEL rl,
45 __in_opt HMODULE hModule,
46 __in DWORD dwLogId,
47 __in va_list args,
48 __in BOOL fLOGUTIL_NEWLINE
49 );
50static HRESULT LogStringWorkArgs(
51 __in REPORT_LEVEL rl,
52 __in_z __format_string LPCSTR szFormat,
53 __in va_list args,
54 __in BOOL fLOGUTIL_NEWLINE
55 );
56static HRESULT LogStringWork(
57 __in REPORT_LEVEL rl,
58 __in DWORD dwLogId,
59 __in_z LPCWSTR sczString,
60 __in BOOL fLOGUTIL_NEWLINE
61 );
62
63// Hook to allow redirecting LogStringWorkRaw function calls
64static PFN_LOGSTRINGWORKRAW s_vpfLogStringWorkRaw = NULL;
65static LPVOID s_vpvLogStringWorkRawContext = NULL;
66
67
68/********************************************************************
69 IsLogInitialized - Checks if log is currently initialized.
70********************************************************************/
71extern "C" BOOL DAPI IsLogInitialized()
72{
73 return LogUtil_fInitializedCriticalSection;
74}
75
76/********************************************************************
77 IsLogOpen - Checks if log is currently initialized and open.
78********************************************************************/
79extern "C" BOOL DAPI IsLogOpen()
80{
81 return (INVALID_HANDLE_VALUE != LogUtil_hLog && NULL != LogUtil_sczLogPath);
82}
83
84
85/********************************************************************
86 LogInitialize - initializes the logutil API
87
88********************************************************************/
89extern "C" void DAPI LogInitialize(
90 __in HMODULE hModule
91 )
92{
93 AssertSz(INVALID_HANDLE_VALUE == LogUtil_hLog && !LogUtil_sczLogPath, "LogInitialize() or LogOpen() - already called.");
94
95 LogUtil_hModule = hModule;
96 LogUtil_fDisabled = FALSE;
97
98 ::InitializeCriticalSection(&LogUtil_csLog);
99 LogUtil_fInitializedCriticalSection = TRUE;
100}
101
102
103/********************************************************************
104 LogOpen - creates an application log file
105
106 NOTE: if wzExt is null then wzLog is path to desired log else wzLog and wzExt are used to generate log name
107********************************************************************/
108extern "C" HRESULT DAPI LogOpen(
109 __in_z_opt LPCWSTR wzDirectory,
110 __in_z LPCWSTR wzLog,
111 __in_z_opt LPCWSTR wzPostfix,
112 __in_z_opt LPCWSTR wzExt,
113 __in BOOL fAppend,
114 __in BOOL fHeader,
115 __out_z_opt LPWSTR* psczLogPath
116 )
117{
118 HRESULT hr = S_OK;
119 BOOL fEnteredCriticalSection = FALSE;
120 LPWSTR sczLogDirectory = NULL;
121
122 ::EnterCriticalSection(&LogUtil_csLog);
123 fEnteredCriticalSection = TRUE;
124
125 if (wzExt && *wzExt)
126 {
127 hr = PathCreateTimeBasedTempFile(wzDirectory, wzLog, wzPostfix, wzExt, &LogUtil_sczLogPath, &LogUtil_hLog);
128 LoguExitOnFailure(hr, "Failed to create log based on current system time.");
129 }
130 else
131 {
132 hr = PathConcat(wzDirectory, wzLog, &LogUtil_sczLogPath);
133 LoguExitOnFailure(hr, "Failed to combine the log path.");
134
135 hr = PathGetDirectory(LogUtil_sczLogPath, &sczLogDirectory);
136 LoguExitOnFailure(hr, "Failed to get log directory.");
137
138 hr = DirEnsureExists(sczLogDirectory, NULL);
139 LoguExitOnFailure(hr, "Failed to ensure log file directory exists: %ls", sczLogDirectory);
140
141 LogUtil_hLog = ::CreateFileW(LogUtil_sczLogPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, (fAppend) ? OPEN_ALWAYS : CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
142 if (INVALID_HANDLE_VALUE == LogUtil_hLog)
143 {
144 LoguExitOnLastError(hr, "failed to create log file: %ls", LogUtil_sczLogPath);
145 }
146
147 if (fAppend)
148 {
149 ::SetFilePointer(LogUtil_hLog, 0, 0, FILE_END);
150 }
151 }
152
153 LogUtil_fDisabled = FALSE;
154
155 if (fHeader)
156 {
157 LogHeader();
158 }
159
160 if (NULL != LogUtil_sczPreInitBuffer)
161 {
162 // Log anything that was logged before LogOpen() was called.
163 LogStringWorkRaw(LogUtil_sczPreInitBuffer);
164 ReleaseNullStr(LogUtil_sczPreInitBuffer);
165 }
166
167 if (psczLogPath)
168 {
169 hr = StrAllocString(psczLogPath, LogUtil_sczLogPath, 0);
170 LoguExitOnFailure(hr, "Failed to copy log path.");
171 }
172
173LExit:
174 if (fEnteredCriticalSection)
175 {
176 ::LeaveCriticalSection(&LogUtil_csLog);
177 }
178
179 ReleaseStr(sczLogDirectory);
180
181 return hr;
182}
183
184
185/********************************************************************
186 LogDisable - closes any open files and disables in memory logging.
187
188********************************************************************/
189void DAPI LogDisable()
190{
191 ::EnterCriticalSection(&LogUtil_csLog);
192
193 LogUtil_fDisabled = TRUE;
194
195 ReleaseFileHandle(LogUtil_hLog);
196 ReleaseNullStr(LogUtil_sczLogPath);
197 ReleaseNullStr(LogUtil_sczPreInitBuffer);
198
199 ::LeaveCriticalSection(&LogUtil_csLog);
200}
201
202
203/********************************************************************
204 LogRedirect - Redirects all logging strings to the specified
205 function - or set NULL to disable the hook
206********************************************************************/
207void DAPI LogRedirect(
208 __in_opt PFN_LOGSTRINGWORKRAW vpfLogStringWorkRaw,
209 __in_opt LPVOID pvContext
210 )
211{
212 s_vpfLogStringWorkRaw = vpfLogStringWorkRaw;
213 s_vpvLogStringWorkRawContext = pvContext;
214}
215
216
217/********************************************************************
218 LogRename - Renames a logfile, moving its contents to a new path,
219 and re-opening the file for appending at the new
220 location
221********************************************************************/
222HRESULT DAPI LogRename(
223 __in_z LPCWSTR wzNewPath
224 )
225{
226 HRESULT hr = S_OK;
227 BOOL fEnteredCriticalSection = FALSE;
228
229 ::EnterCriticalSection(&LogUtil_csLog);
230 fEnteredCriticalSection = TRUE;
231
232 ReleaseFileHandle(LogUtil_hLog);
233
234 hr = FileEnsureMove(LogUtil_sczLogPath, wzNewPath, TRUE, TRUE);
235 LoguExitOnFailure(hr, "Failed to move logfile to new location: %ls", wzNewPath);
236
237 hr = StrAllocString(&LogUtil_sczLogPath, wzNewPath, 0);
238 LoguExitOnFailure(hr, "Failed to store new logfile path: %ls", wzNewPath);
239
240 LogUtil_hLog = ::CreateFileW(LogUtil_sczLogPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
241 if (INVALID_HANDLE_VALUE == LogUtil_hLog)
242 {
243 LoguExitOnLastError(hr, "failed to create log file: %ls", LogUtil_sczLogPath);
244 }
245
246 // Enable "append" mode by moving file pointer to the end
247 ::SetFilePointer(LogUtil_hLog, 0, 0, FILE_END);
248
249LExit:
250 if (fEnteredCriticalSection)
251 {
252 ::LeaveCriticalSection(&LogUtil_csLog);
253 }
254
255 return hr;
256}
257
258
259extern "C" void DAPI LogClose(
260 __in BOOL fFooter
261 )
262{
263 if (INVALID_HANDLE_VALUE != LogUtil_hLog && fFooter)
264 {
265 LogFooter();
266 }
267
268 ReleaseFileHandle(LogUtil_hLog);
269 ReleaseNullStr(LogUtil_sczLogPath);
270 ReleaseNullStr(LogUtil_sczPreInitBuffer);
271}
272
273
274extern "C" void DAPI LogUninitialize(
275 __in BOOL fFooter
276 )
277{
278 LogClose(fFooter);
279
280 if (LogUtil_fInitializedCriticalSection)
281 {
282 ::DeleteCriticalSection(&LogUtil_csLog);
283 LogUtil_fInitializedCriticalSection = FALSE;
284 }
285
286 LogUtil_hModule = NULL;
287 LogUtil_fDisabled = FALSE;
288
289 ReleaseNullStr(LogUtil_sczSpecialBeginLine);
290 ReleaseNullStr(LogUtil_sczSpecialAfterTimeStamp);
291 ReleaseNullStr(LogUtil_sczSpecialEndLine);
292}
293
294
295/********************************************************************
296 LogIsOpen - returns whether log file is open or note
297
298********************************************************************/
299extern "C" BOOL DAPI LogIsOpen()
300{
301 return INVALID_HANDLE_VALUE != LogUtil_hLog;
302}
303
304
305/********************************************************************
306 LogSetSpecialParams - sets a special beginline string, endline
307 string, post-timestamp string, etc.
308********************************************************************/
309HRESULT DAPI LogSetSpecialParams(
310 __in_z_opt LPCWSTR wzSpecialBeginLine,
311 __in_z_opt LPCWSTR wzSpecialAfterTimeStamp,
312 __in_z_opt LPCWSTR wzSpecialEndLine
313 )
314{
315 HRESULT hr = S_OK;
316
317 // Handle special string to be prepended before every full line
318 if (NULL == wzSpecialBeginLine)
319 {
320 ReleaseNullStr(LogUtil_sczSpecialBeginLine);
321 }
322 else
323 {
324 hr = StrAllocConcat(&LogUtil_sczSpecialBeginLine, wzSpecialBeginLine, 0);
325 LoguExitOnFailure(hr, "Failed to allocate copy of special beginline string");
326 }
327
328 // Handle special string to be appended to every time stamp
329 if (NULL == wzSpecialAfterTimeStamp)
330 {
331 ReleaseNullStr(LogUtil_sczSpecialAfterTimeStamp);
332 }
333 else
334 {
335 hr = StrAllocConcat(&LogUtil_sczSpecialAfterTimeStamp, wzSpecialAfterTimeStamp, 0);
336 LoguExitOnFailure(hr, "Failed to allocate copy of special post-timestamp string");
337 }
338
339 // Handle special string to be appended before every full line
340 if (NULL == wzSpecialEndLine)
341 {
342 ReleaseNullStr(LogUtil_sczSpecialEndLine);
343 }
344 else
345 {
346 hr = StrAllocConcat(&LogUtil_sczSpecialEndLine, wzSpecialEndLine, 0);
347 LoguExitOnFailure(hr, "Failed to allocate copy of special endline string");
348 }
349
350LExit:
351 return hr;
352}
353
354/********************************************************************
355 LogSetLevel - sets the logging level
356
357 NOTE: returns previous logging level
358********************************************************************/
359extern "C" REPORT_LEVEL DAPI LogSetLevel(
360 __in REPORT_LEVEL rl,
361 __in BOOL fLogChange
362 )
363{
364 AssertSz(REPORT_ERROR != rl, "REPORT_ERROR is not a valid logging level to set");
365
366 REPORT_LEVEL rlPrev = LogUtil_rlCurrent;
367
368 if (LogUtil_rlCurrent != rl)
369 {
370 LogUtil_rlCurrent = rl;
371
372 if (fLogChange)
373 {
374 LPCSTR szLevel = LOGUTIL_UNKNOWN;
375 switch (LogUtil_rlCurrent)
376 {
377 case REPORT_WARNING:
378 szLevel = LOGUTIL_WARNING;
379 break;
380 case REPORT_STANDARD:
381 szLevel = LOGUTIL_STANDARD;
382 break;
383 case REPORT_VERBOSE:
384 szLevel = LOGUTIL_VERBOSE;
385 break;
386 case REPORT_DEBUG:
387 szLevel = LOGUTIL_DEBUG;
388 break;
389 case REPORT_NONE:
390 szLevel = LOGUTIL_NONE;
391 break;
392 }
393
394 LogStringLine(REPORT_STANDARD, "--- logging level: %hs ---", szLevel);
395 }
396 }
397
398 return rlPrev;
399}
400
401
402/********************************************************************
403 LogGetLevel - gets the current logging level
404
405********************************************************************/
406extern "C" REPORT_LEVEL DAPI LogGetLevel()
407{
408 return LogUtil_rlCurrent;
409}
410
411
412/********************************************************************
413 LogGetPath - gets the current log path
414
415********************************************************************/
416extern "C" HRESULT DAPI LogGetPath(
417 __out_ecount_z(cchLogPath) LPWSTR pwzLogPath,
418 __in DWORD cchLogPath
419 )
420{
421 Assert(pwzLogPath);
422
423 HRESULT hr = S_OK;
424
425 if (NULL == LogUtil_sczLogPath) // they can't have a path if there isn't one!
426 {
427 ExitFunction1(hr = E_UNEXPECTED);
428 }
429
430 hr = ::StringCchCopyW(pwzLogPath, cchLogPath, LogUtil_sczLogPath);
431
432LExit:
433 return hr;
434}
435
436
437/********************************************************************
438 LogGetHandle - gets the current log file handle
439
440********************************************************************/
441extern "C" HANDLE DAPI LogGetHandle()
442{
443 return LogUtil_hLog;
444}
445
446
447/********************************************************************
448 LogString - write a string to the log
449
450 NOTE: use printf formatting ("%ls", "%d", etc.)
451********************************************************************/
452extern "C" HRESULT DAPIV LogString(
453 __in REPORT_LEVEL rl,
454 __in_z __format_string LPCSTR szFormat,
455 ...
456 )
457{
458 HRESULT hr = S_OK;
459 va_list args;
460
461 va_start(args, szFormat);
462 hr = LogStringArgs(rl, szFormat, args);
463 va_end(args);
464
465 return hr;
466}
467
468extern "C" HRESULT DAPI LogStringArgs(
469 __in REPORT_LEVEL rl,
470 __in_z __format_string LPCSTR szFormat,
471 __in va_list args
472 )
473{
474 AssertSz(REPORT_NONE != rl, "REPORT_NONE is not a valid logging level");
475 HRESULT hr = S_OK;
476
477 if (REPORT_ERROR != rl && LogUtil_rlCurrent < rl)
478 {
479 ExitFunction1(hr = S_FALSE);
480 }
481
482 hr = LogStringWorkArgs(rl, szFormat, args, FALSE);
483
484LExit:
485 return hr;
486}
487
488/********************************************************************
489 LogStringLine - write a string plus LOGUTIL_NEWLINE to the log
490
491 NOTE: use printf formatting ("%ls", "%d", etc.)
492********************************************************************/
493extern "C" HRESULT DAPIV LogStringLine(
494 __in REPORT_LEVEL rl,
495 __in_z __format_string LPCSTR szFormat,
496 ...
497 )
498{
499 HRESULT hr = S_OK;
500 va_list args;
501
502 va_start(args, szFormat);
503 hr = LogStringLineArgs(rl, szFormat, args);
504 va_end(args);
505
506 return hr;
507}
508
509extern "C" HRESULT DAPI LogStringLineArgs(
510 __in REPORT_LEVEL rl,
511 __in_z __format_string LPCSTR szFormat,
512 __in va_list args
513 )
514{
515 AssertSz(REPORT_NONE != rl, "REPORT_NONE is not a valid logging level");
516 HRESULT hr = S_OK;
517
518 if (REPORT_ERROR != rl && LogUtil_rlCurrent < rl)
519 {
520 ExitFunction1(hr = S_FALSE);
521 }
522
523 hr = LogStringWorkArgs(rl, szFormat, args, TRUE);
524
525LExit:
526 return hr;
527}
528
529/********************************************************************
530 LogIdModuleArgs - write a string embedded in a MESSAGETABLE to the log
531
532 NOTE: uses format string from MESSAGETABLE resource
533********************************************************************/
534
535extern "C" HRESULT DAPI LogIdModuleArgs(
536 __in REPORT_LEVEL rl,
537 __in DWORD dwLogId,
538 __in_opt HMODULE hModule,
539 __in va_list args
540 )
541{
542 AssertSz(REPORT_NONE != rl, "REPORT_NONE is not a valid logging level");
543 HRESULT hr = S_OK;
544
545 if (REPORT_ERROR != rl && LogUtil_rlCurrent < rl)
546 {
547 ExitFunction1(hr = S_FALSE);
548 }
549
550 hr = LogIdWork(rl, (hModule) ? hModule : LogUtil_hModule, dwLogId, args, TRUE);
551
552LExit:
553 return hr;
554}
555
556extern "C" HRESULT DAPI LogIdModule(
557 __in REPORT_LEVEL rl,
558 __in DWORD dwLogId,
559 __in_opt HMODULE hModule,
560 ...
561 )
562{
563 AssertSz(REPORT_NONE != rl, "REPORT_NONE is not a valid logging level");
564 HRESULT hr = S_OK;
565 va_list args;
566
567 if (REPORT_ERROR != rl && LogUtil_rlCurrent < rl)
568 {
569 ExitFunction1(hr = S_FALSE);
570 }
571
572 va_start(args, hModule);
573 hr = LogIdWork(rl, (hModule) ? hModule : LogUtil_hModule, dwLogId, args, TRUE);
574 va_end(args);
575
576LExit:
577 return hr;
578}
579
580
581
582
583/********************************************************************
584 LogError - write an error to the log
585
586 NOTE: use printf formatting ("%ls", "%d", etc.)
587********************************************************************/
588extern "C" HRESULT DAPIV LogErrorString(
589 __in HRESULT hrError,
590 __in_z __format_string LPCSTR szFormat,
591 ...
592 )
593{
594 HRESULT hr = S_OK;
595
596 va_list args;
597 va_start(args, szFormat);
598 hr = LogErrorStringArgs(hrError, szFormat, args);
599 va_end(args);
600
601 return hr;
602}
603
604extern "C" HRESULT DAPI LogErrorStringArgs(
605 __in HRESULT hrError,
606 __in_z __format_string LPCSTR szFormat,
607 __in va_list args
608 )
609{
610 HRESULT hr = S_OK;
611 LPWSTR sczFormat = NULL;
612 LPWSTR sczMessage = NULL;
613
614 hr = StrAllocStringAnsi(&sczFormat, szFormat, 0, CP_ACP);
615 LoguExitOnFailure(hr, "Failed to convert format string to wide character string");
616
617 // format the string as a unicode string - this is necessary to be able to include
618 // international characters in our output string. This does have the counterintuitive effect
619 // that the caller's "%s" is interpreted differently
620 // (so callers should use %hs for LPSTR and %ls for LPWSTR)
621 hr = StrAllocFormattedArgs(&sczMessage, sczFormat, args);
622 LoguExitOnFailure(hr, "Failed to format error message: \"%ls\"", sczFormat);
623
624 hr = LogStringLine(REPORT_ERROR, "Error 0x%x: %ls", hrError, sczMessage);
625
626LExit:
627 ReleaseStr(sczFormat);
628 ReleaseStr(sczMessage);
629
630 return hr;
631}
632
633
634/********************************************************************
635 LogErrorIdModule - write an error string embedded in a MESSAGETABLE to the log
636
637 NOTE: uses format string from MESSAGETABLE resource
638 can log no more than three strings in the error message
639********************************************************************/
640extern "C" HRESULT DAPI LogErrorIdModule(
641 __in HRESULT hrError,
642 __in DWORD dwLogId,
643 __in_opt HMODULE hModule,
644 __in_z_opt LPCWSTR wzString1 = NULL,
645 __in_z_opt LPCWSTR wzString2 = NULL,
646 __in_z_opt LPCWSTR wzString3 = NULL
647 )
648{
649 HRESULT hr = S_OK;
650 WCHAR wzError[11];
651 WORD cStrings = 1; // guaranteed wzError is in the list
652
653 hr = ::StringCchPrintfW(wzError, countof(wzError), L"0x%08x", hrError);
654 LoguExitOnFailure(hr, "failed to format error code: \"0%08x\"", hrError);
655
656 cStrings += wzString1 ? 1 : 0;
657 cStrings += wzString2 ? 1 : 0;
658 cStrings += wzString3 ? 1 : 0;
659
660 hr = LogIdModule(REPORT_ERROR, dwLogId, hModule, wzError, wzString1, wzString2, wzString3);
661 LoguExitOnFailure(hr, "Failed to log id module.");
662
663LExit:
664 return hr;
665}
666
667/********************************************************************
668 LogHeader - write a standard header to the log
669
670********************************************************************/
671extern "C" HRESULT DAPI LogHeader()
672{
673 HRESULT hr = S_OK;
674 WCHAR wzComputerName[MAX_PATH];
675 DWORD cchComputerName = countof(wzComputerName);
676 WCHAR wzPath[MAX_PATH];
677 DWORD dwMajorVersion = 0;
678 DWORD dwMinorVersion = 0;
679 LPCSTR szLevel = LOGUTIL_UNKNOWN;
680 LPWSTR sczCurrentDateTime = NULL;
681
682 //
683 // get the interesting data
684 //
685 if (!::GetModuleFileNameW(NULL, wzPath, countof(wzPath)))
686 {
687 memset(wzPath, 0, sizeof(wzPath));
688 }
689
690 hr = FileVersion(wzPath, &dwMajorVersion, &dwMinorVersion);
691 if (FAILED(hr))
692 {
693 dwMajorVersion = 0;
694 dwMinorVersion = 0;
695 }
696
697 if (!::GetComputerNameW(wzComputerName, &cchComputerName))
698 {
699 ::SecureZeroMemory(wzComputerName, sizeof(wzComputerName));
700 }
701
702 TimeCurrentDateTime(&sczCurrentDateTime, FALSE);
703
704 //
705 // write data to the log
706 //
707 LogStringLine(REPORT_STANDARD, "=== Logging started: %ls ===", sczCurrentDateTime);
708 LogStringLine(REPORT_STANDARD, "Executable: %ls v%d.%d.%d.%d", wzPath, dwMajorVersion >> 16, dwMajorVersion & 0xFFFF, dwMinorVersion >> 16, dwMinorVersion & 0xFFFF);
709 LogStringLine(REPORT_STANDARD, "Computer : %ls", wzComputerName);
710 switch (LogUtil_rlCurrent)
711 {
712 case REPORT_WARNING:
713 szLevel = LOGUTIL_WARNING;
714 break;
715 case REPORT_STANDARD:
716 szLevel = LOGUTIL_STANDARD;
717 break;
718 case REPORT_VERBOSE:
719 szLevel = LOGUTIL_VERBOSE;
720 break;
721 case REPORT_DEBUG:
722 szLevel = LOGUTIL_DEBUG;
723 break;
724 case REPORT_NONE:
725 szLevel = LOGUTIL_NONE;
726 break;
727 }
728 LogStringLine(REPORT_STANDARD, "--- logging level: %hs ---", szLevel);
729
730 hr = S_OK;
731
732 ReleaseStr(sczCurrentDateTime);
733
734 return hr;
735}
736
737
738/********************************************************************
739 LogFooterWork - write a standard footer to the log
740
741********************************************************************/
742
743static HRESULT LogFooterWork(
744 __in_z __format_string LPCSTR szFormat,
745 ...
746 )
747{
748 HRESULT hr = S_OK;
749
750 va_list args;
751 va_start(args, szFormat);
752 hr = LogStringWorkArgs(REPORT_STANDARD, szFormat, args, TRUE);
753 va_end(args);
754
755 return hr;
756}
757
758extern "C" HRESULT DAPI LogFooter()
759{
760 HRESULT hr = S_OK;
761 LPWSTR sczCurrentDateTime = NULL;
762 TimeCurrentDateTime(&sczCurrentDateTime, FALSE);
763 hr = LogFooterWork("=== Logging stopped: %ls ===", sczCurrentDateTime);
764 ReleaseStr(sczCurrentDateTime);
765 return hr;
766}
767
768/********************************************************************
769 LogStringWorkRaw - Write a raw, unformatted string to the log
770
771********************************************************************/
772extern "C" HRESULT LogStringWorkRaw(
773 __in_z LPCSTR szLogData
774 )
775{
776 Assert(szLogData && *szLogData);
777
778 HRESULT hr = S_OK;
779 size_t cchLogData = 0;
780 DWORD cbLogData = 0;
781 DWORD cbTotal = 0;
782 DWORD cbWrote = 0;
783
784 hr = ::StringCchLengthA(szLogData, STRSAFE_MAX_CCH, &cchLogData);
785 LoguExitOnRootFailure(hr, "Failed to get length of raw string");
786
787 cbLogData = (DWORD)cchLogData;
788
789 // If the log hasn't been initialized yet, store it in a buffer
790 if (INVALID_HANDLE_VALUE == LogUtil_hLog)
791 {
792 hr = StrAnsiAllocConcat(&LogUtil_sczPreInitBuffer, szLogData, 0);
793 LoguExitOnFailure(hr, "Failed to concatenate string to pre-init buffer");
794
795 ExitFunction1(hr = S_OK);
796 }
797
798 // write the string
799 while (cbTotal < cbLogData)
800 {
801 if (!::WriteFile(LogUtil_hLog, reinterpret_cast<const BYTE*>(szLogData) + cbTotal, cbLogData - cbTotal, &cbWrote, NULL))
802 {
803 LoguExitOnLastError(hr, "Failed to write output to log: %ls - %hs", LogUtil_sczLogPath, szLogData);
804 }
805
806 cbTotal += cbWrote;
807 }
808
809LExit:
810 return hr;
811}
812
813//
814// private worker functions
815//
816static HRESULT LogIdWork(
817 __in REPORT_LEVEL rl,
818 __in_opt HMODULE hModule,
819 __in DWORD dwLogId,
820 __in va_list args,
821 __in BOOL fLOGUTIL_NEWLINE
822 )
823{
824 HRESULT hr = S_OK;
825 LPWSTR pwz = NULL;
826 DWORD cch = 0;
827
828 // get the string for the id
829#pragma prefast(push)
830#pragma prefast(disable:25028)
831#pragma prefast(disable:25068)
832 cch = ::FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE,
833 static_cast<LPCVOID>(hModule), dwLogId, 0, reinterpret_cast<LPWSTR>(&pwz), 0, &args);
834#pragma prefast(pop)
835
836 if (0 == cch)
837 {
838 LoguExitOnLastError(hr, "failed to log id: %d", dwLogId);
839 }
840
841 if (2 <= cch && L'\r' == pwz[cch-2] && L'\n' == pwz[cch-1])
842 {
843 pwz[cch-2] = L'\0'; // remove newline from message table
844 }
845
846 LogStringWork(rl, dwLogId, pwz, fLOGUTIL_NEWLINE);
847
848LExit:
849 if (pwz)
850 {
851 ::LocalFree(pwz);
852 }
853
854 return hr;
855}
856
857
858static HRESULT LogStringWorkArgs(
859 __in REPORT_LEVEL rl,
860 __in_z __format_string LPCSTR szFormat,
861 __in va_list args,
862 __in BOOL fLOGUTIL_NEWLINE
863 )
864{
865 Assert(szFormat && *szFormat);
866
867 HRESULT hr = S_OK;
868 LPWSTR sczFormat = NULL;
869 LPWSTR sczMessage = NULL;
870
871 hr = StrAllocStringAnsi(&sczFormat, szFormat, 0, CP_ACP);
872 LoguExitOnFailure(hr, "Failed to convert format string to wide character string");
873
874 // format the string as a unicode string
875 hr = StrAllocFormattedArgs(&sczMessage, sczFormat, args);
876 LoguExitOnFailure(hr, "Failed to format message: \"%ls\"", sczFormat);
877
878 hr = LogStringWork(rl, 0, sczMessage, fLOGUTIL_NEWLINE);
879 LoguExitOnFailure(hr, "Failed to write formatted string to log:%ls", sczMessage);
880
881LExit:
882 ReleaseStr(sczFormat);
883 ReleaseStr(sczMessage);
884
885 return hr;
886}
887
888
889static HRESULT LogStringWork(
890 __in REPORT_LEVEL rl,
891 __in DWORD dwLogId,
892 __in_z LPCWSTR sczString,
893 __in BOOL fLOGUTIL_NEWLINE
894 )
895{
896 Assert(sczString && *sczString);
897
898 HRESULT hr = S_OK;
899 BOOL fEnteredCriticalSection = FALSE;
900 LPWSTR scz = NULL;
901 LPCWSTR wzLogData = NULL;
902 LPSTR sczMultiByte = NULL;
903
904 // If logging is disabled, just bail.
905 if (LogUtil_fDisabled)
906 {
907 ExitFunction();
908 }
909
910 ::EnterCriticalSection(&LogUtil_csLog);
911 fEnteredCriticalSection = TRUE;
912
913 if (fLOGUTIL_NEWLINE)
914 {
915 // get the process and thread id.
916 DWORD dwProcessId = ::GetCurrentProcessId();
917 DWORD dwThreadId = ::GetCurrentThreadId();
918
919 // get the time relative to GMT.
920 SYSTEMTIME st = { };
921 ::GetLocalTime(&st);
922
923 DWORD dwId = dwLogId & 0xFFFFFFF;
924 DWORD dwType = dwLogId & 0xF0000000;
925 LPSTR szType = (0xE0000000 == dwType || REPORT_ERROR == rl) ? "e" : (0xA0000000 == dwType || REPORT_WARNING == rl) ? "w" : "i";
926
927 // add line prefix and trailing newline
928 hr = StrAllocFormatted(&scz, L"%ls[%04X:%04X][%04hu-%02hu-%02huT%02hu:%02hu:%02hu]%hs%03d:%ls %ls%ls", LogUtil_sczSpecialBeginLine ? LogUtil_sczSpecialBeginLine : L"",
929 dwProcessId, dwThreadId, st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, szType, dwId,
930 LogUtil_sczSpecialAfterTimeStamp ? LogUtil_sczSpecialAfterTimeStamp : L"", sczString, LogUtil_sczSpecialEndLine ? LogUtil_sczSpecialEndLine : L"\r\n");
931 LoguExitOnFailure(hr, "Failed to format line prefix.");
932 }
933
934 wzLogData = scz ? scz : sczString;
935
936 // Convert to UTF-8 before writing out to the log file
937 hr = StrAnsiAllocString(&sczMultiByte, wzLogData, 0, CP_UTF8);
938 LoguExitOnFailure(hr, "Failed to convert log string to UTF-8");
939
940 if (s_vpfLogStringWorkRaw)
941 {
942 hr = s_vpfLogStringWorkRaw(sczMultiByte, s_vpvLogStringWorkRawContext);
943 LoguExitOnFailure(hr, "Failed to write string to log using redirected function: %ls", sczString);
944 }
945 else
946 {
947 hr = LogStringWorkRaw(sczMultiByte);
948 LoguExitOnFailure(hr, "Failed to write string to log using default function: %ls", sczString);
949 }
950
951LExit:
952 if (fEnteredCriticalSection)
953 {
954 ::LeaveCriticalSection(&LogUtil_csLog);
955 }
956
957 ReleaseStr(scz);
958 ReleaseStr(sczMultiByte);
959
960 return hr;
961}
diff --git a/src/libs/dutil/WixToolset.DUtil/memutil.cpp b/src/libs/dutil/WixToolset.DUtil/memutil.cpp
new file mode 100644
index 00000000..c805a9c0
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/memutil.cpp
@@ -0,0 +1,336 @@
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// Exit macros
7#define MemExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_MEMUTIL, x, s, __VA_ARGS__)
8#define MemExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_MEMUTIL, x, s, __VA_ARGS__)
9#define MemExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_MEMUTIL, x, s, __VA_ARGS__)
10#define MemExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_MEMUTIL, x, s, __VA_ARGS__)
11#define MemExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_MEMUTIL, x, s, __VA_ARGS__)
12#define MemExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_MEMUTIL, x, s, __VA_ARGS__)
13#define MemExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_MEMUTIL, p, x, e, s, __VA_ARGS__)
14#define MemExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_MEMUTIL, p, x, s, __VA_ARGS__)
15#define MemExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_MEMUTIL, p, x, e, s, __VA_ARGS__)
16#define MemExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_MEMUTIL, p, x, s, __VA_ARGS__)
17#define MemExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_MEMUTIL, e, x, s, __VA_ARGS__)
18#define MemExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_MEMUTIL, g, x, s, __VA_ARGS__)
19
20
21#if DEBUG
22static BOOL vfMemInitialized = FALSE;
23#endif
24
25extern "C" HRESULT DAPI MemInitialize()
26{
27#if DEBUG
28 vfMemInitialized = TRUE;
29#endif
30 return S_OK;
31}
32
33extern "C" void DAPI MemUninitialize()
34{
35#if DEBUG
36 vfMemInitialized = FALSE;
37#endif
38}
39
40extern "C" LPVOID DAPI MemAlloc(
41 __in SIZE_T cbSize,
42 __in BOOL fZero
43 )
44{
45// AssertSz(vfMemInitialized, "MemInitialize() not called, this would normally crash");
46 AssertSz(0 < cbSize, "MemAlloc() called with invalid size");
47 return ::HeapAlloc(::GetProcessHeap(), fZero ? HEAP_ZERO_MEMORY : 0, cbSize);
48}
49
50
51extern "C" LPVOID DAPI MemReAlloc(
52 __in LPVOID pv,
53 __in SIZE_T cbSize,
54 __in BOOL fZero
55 )
56{
57// AssertSz(vfMemInitialized, "MemInitialize() not called, this would normally crash");
58 AssertSz(0 < cbSize, "MemReAlloc() called with invalid size");
59 return ::HeapReAlloc(::GetProcessHeap(), fZero ? HEAP_ZERO_MEMORY : 0, pv, cbSize);
60}
61
62
63extern "C" HRESULT DAPI MemReAllocSecure(
64 __in LPVOID pv,
65 __in SIZE_T cbSize,
66 __in BOOL fZero,
67 __deref_out LPVOID* ppvNew
68 )
69{
70// AssertSz(vfMemInitialized, "MemInitialize() not called, this would normally crash");
71 AssertSz(ppvNew, "MemReAllocSecure() called with uninitialized pointer");
72 AssertSz(0 < cbSize, "MemReAllocSecure() called with invalid size");
73
74 HRESULT hr = S_OK;
75 DWORD dwFlags = HEAP_REALLOC_IN_PLACE_ONLY;
76 LPVOID pvNew = NULL;
77
78 dwFlags |= fZero ? HEAP_ZERO_MEMORY : 0;
79 pvNew = ::HeapReAlloc(::GetProcessHeap(), dwFlags, pv, cbSize);
80 if (!pvNew)
81 {
82 pvNew = MemAlloc(cbSize, fZero);
83 if (pvNew)
84 {
85 const SIZE_T cbCurrent = MemSize(pv);
86 if (-1 == cbCurrent)
87 {
88 MemExitOnRootFailure(hr = E_INVALIDARG, "Failed to get memory size");
89 }
90
91 // HeapReAlloc may allocate more memory than requested.
92 const SIZE_T cbNew = MemSize(pvNew);
93 if (-1 == cbNew)
94 {
95 MemExitOnRootFailure(hr = E_INVALIDARG, "Failed to get memory size");
96 }
97
98 cbSize = cbNew;
99 if (cbSize > cbCurrent)
100 {
101 cbSize = cbCurrent;
102 }
103
104 memcpy_s(pvNew, cbNew, pv, cbSize);
105
106 SecureZeroMemory(pv, cbCurrent);
107 MemFree(pv);
108 }
109 }
110 MemExitOnNull(pvNew, hr, E_OUTOFMEMORY, "Failed to reallocate memory");
111
112 *ppvNew = pvNew;
113 pvNew = NULL;
114
115LExit:
116 ReleaseMem(pvNew);
117
118 return hr;
119}
120
121
122extern "C" HRESULT DAPI MemAllocArray(
123 __inout LPVOID* ppvArray,
124 __in SIZE_T cbArrayType,
125 __in DWORD dwItemCount
126 )
127{
128 return MemReAllocArray(ppvArray, 0, cbArrayType, dwItemCount);
129}
130
131
132extern "C" HRESULT DAPI MemReAllocArray(
133 __inout LPVOID* ppvArray,
134 __in DWORD cArray,
135 __in SIZE_T cbArrayType,
136 __in DWORD dwNewItemCount
137 )
138{
139 HRESULT hr = S_OK;
140 DWORD cNew = 0;
141 LPVOID pvNew = NULL;
142 SIZE_T cbNew = 0;
143
144 hr = ::DWordAdd(cArray, dwNewItemCount, &cNew);
145 MemExitOnFailure(hr, "Integer overflow when calculating new element count.");
146
147 hr = ::SIZETMult(cNew, cbArrayType, &cbNew);
148 MemExitOnFailure(hr, "Integer overflow when calculating new block size.");
149
150 if (*ppvArray)
151 {
152 SIZE_T cbCurrent = MemSize(*ppvArray);
153 if (cbCurrent < cbNew)
154 {
155 pvNew = MemReAlloc(*ppvArray, cbNew, TRUE);
156 MemExitOnNull(pvNew, hr, E_OUTOFMEMORY, "Failed to allocate larger array.");
157
158 *ppvArray = pvNew;
159 }
160 }
161 else
162 {
163 pvNew = MemAlloc(cbNew, TRUE);
164 MemExitOnNull(pvNew, hr, E_OUTOFMEMORY, "Failed to allocate new array.");
165
166 *ppvArray = pvNew;
167 }
168
169LExit:
170 return hr;
171}
172
173
174extern "C" HRESULT DAPI MemEnsureArraySize(
175 __deref_inout_bcount(cArray * cbArrayType) LPVOID* ppvArray,
176 __in DWORD cArray,
177 __in SIZE_T cbArrayType,
178 __in DWORD dwGrowthCount
179 )
180{
181 HRESULT hr = S_OK;
182 DWORD cNew = 0;
183 LPVOID pvNew = NULL;
184 SIZE_T cbNew = 0;
185
186 hr = ::DWordAdd(cArray, dwGrowthCount, &cNew);
187 MemExitOnFailure(hr, "Integer overflow when calculating new element count.");
188
189 hr = ::SIZETMult(cNew, cbArrayType, &cbNew);
190 MemExitOnFailure(hr, "Integer overflow when calculating new block size.");
191
192 if (*ppvArray)
193 {
194 SIZE_T cbUsed = cArray * cbArrayType;
195 SIZE_T cbCurrent = MemSize(*ppvArray);
196 if (cbCurrent < cbUsed)
197 {
198 pvNew = MemReAlloc(*ppvArray, cbNew, TRUE);
199 MemExitOnNull(pvNew, hr, E_OUTOFMEMORY, "Failed to allocate array larger.");
200
201 *ppvArray = pvNew;
202 }
203 }
204 else
205 {
206 pvNew = MemAlloc(cbNew, TRUE);
207 MemExitOnNull(pvNew, hr, E_OUTOFMEMORY, "Failed to allocate new array.");
208
209 *ppvArray = pvNew;
210 }
211
212LExit:
213 return hr;
214}
215
216
217extern "C" HRESULT DAPI MemInsertIntoArray(
218 __deref_inout_bcount((cExistingArray + cInsertItems) * cbArrayType) LPVOID* ppvArray,
219 __in DWORD dwInsertIndex,
220 __in DWORD cInsertItems,
221 __in DWORD cExistingArray,
222 __in SIZE_T cbArrayType,
223 __in DWORD dwGrowthCount
224 )
225{
226 HRESULT hr = S_OK;
227 DWORD i;
228 BYTE *pbArray = NULL;
229
230 if (0 == cInsertItems)
231 {
232 ExitFunction1(hr = S_OK);
233 }
234
235 hr = MemEnsureArraySize(ppvArray, cExistingArray + cInsertItems, cbArrayType, dwGrowthCount);
236 MemExitOnFailure(hr, "Failed to resize array while inserting items");
237
238 pbArray = reinterpret_cast<BYTE *>(*ppvArray);
239 for (i = cExistingArray + cInsertItems - 1; i > dwInsertIndex; --i)
240 {
241 memcpy_s(pbArray + i * cbArrayType, cbArrayType, pbArray + (i - 1) * cbArrayType, cbArrayType);
242 }
243
244 // Zero out the newly-inserted items
245 memset(pbArray + dwInsertIndex * cbArrayType, 0, cInsertItems * cbArrayType);
246
247LExit:
248 return hr;
249}
250
251extern "C" void DAPI MemRemoveFromArray(
252 __inout_bcount((cExistingArray) * cbArrayType) LPVOID pvArray,
253 __in DWORD dwRemoveIndex,
254 __in DWORD cRemoveItems,
255 __in DWORD cExistingArray,
256 __in SIZE_T cbArrayType,
257 __in BOOL fPreserveOrder
258 )
259{
260 BYTE *pbArray = static_cast<BYTE *>(pvArray);
261 DWORD cItemsLeftAfterRemoveIndex = (cExistingArray - cRemoveItems - dwRemoveIndex);
262
263 if (fPreserveOrder)
264 {
265 memmove(pbArray + dwRemoveIndex * cbArrayType, pbArray + (dwRemoveIndex + cRemoveItems) * cbArrayType, cItemsLeftAfterRemoveIndex * cbArrayType);
266 }
267 else
268 {
269 DWORD cItemsToMove = (cRemoveItems > cItemsLeftAfterRemoveIndex ? cItemsLeftAfterRemoveIndex : cRemoveItems);
270 memmove(pbArray + dwRemoveIndex * cbArrayType, pbArray + (cExistingArray - cItemsToMove) * cbArrayType, cItemsToMove * cbArrayType);
271 }
272
273 ZeroMemory(pbArray + (cExistingArray - cRemoveItems) * cbArrayType, cRemoveItems * cbArrayType);
274}
275
276extern "C" void DAPI MemArraySwapItems(
277 __inout_bcount(cbArrayType) LPVOID pvArray,
278 __in DWORD dwIndex1,
279 __in DWORD dwIndex2,
280 __in SIZE_T cbArrayType
281 )
282{
283 BYTE *pbArrayItem1 = static_cast<BYTE *>(pvArray) + dwIndex1 * cbArrayType;
284 BYTE *pbArrayItem2 = static_cast<BYTE *>(pvArray) + dwIndex2 * cbArrayType;
285 DWORD dwByteIndex = 0;
286
287 if (dwIndex1 == dwIndex2)
288 {
289 return;
290 }
291
292 // Use XOR swapping to avoid the need for a temporary item
293 while (dwByteIndex < cbArrayType)
294 {
295 // Try to do many bytes at a time in most cases
296 if (cbArrayType - dwByteIndex > sizeof(DWORD64))
297 {
298 // x: X xor Y
299 *(reinterpret_cast<DWORD64 *>(pbArrayItem1 + dwByteIndex)) ^= *(reinterpret_cast<DWORD64 *>(pbArrayItem2 + dwByteIndex));
300 // y: X xor Y
301 *(reinterpret_cast<DWORD64 *>(pbArrayItem2 + dwByteIndex)) = *(reinterpret_cast<DWORD64 *>(pbArrayItem1 + dwByteIndex)) ^ *(reinterpret_cast<DWORD64 *>(pbArrayItem2 + dwByteIndex));
302 // x: X xor Y
303 *(reinterpret_cast<DWORD64 *>(pbArrayItem1 + dwByteIndex)) ^= *(reinterpret_cast<DWORD64 *>(pbArrayItem2 + dwByteIndex));
304
305 dwByteIndex += sizeof(DWORD64);
306 }
307 else
308 {
309 // x: X xor Y
310 *(reinterpret_cast<unsigned char *>(pbArrayItem1 + dwByteIndex)) ^= *(reinterpret_cast<unsigned char *>(pbArrayItem2 + dwByteIndex));
311 // y: X xor Y
312 *(reinterpret_cast<unsigned char *>(pbArrayItem2 + dwByteIndex)) = *(reinterpret_cast<unsigned char *>(pbArrayItem1 + dwByteIndex)) ^ *(reinterpret_cast<unsigned char *>(pbArrayItem2 + dwByteIndex));
313 // x: X xor Y
314 *(reinterpret_cast<unsigned char *>(pbArrayItem1 + dwByteIndex)) ^= *(reinterpret_cast<unsigned char *>(pbArrayItem2 + dwByteIndex));
315
316 dwByteIndex += sizeof(unsigned char);
317 }
318 }
319}
320
321extern "C" HRESULT DAPI MemFree(
322 __in LPVOID pv
323 )
324{
325// AssertSz(vfMemInitialized, "MemInitialize() not called, this would normally crash");
326 return ::HeapFree(::GetProcessHeap(), 0, pv) ? S_OK : HRESULT_FROM_WIN32(::GetLastError());
327}
328
329
330extern "C" SIZE_T DAPI MemSize(
331 __in LPCVOID pv
332 )
333{
334// AssertSz(vfMemInitialized, "MemInitialize() not called, this would normally crash");
335 return ::HeapSize(::GetProcessHeap(), 0, pv);
336}
diff --git a/src/libs/dutil/WixToolset.DUtil/metautil.cpp b/src/libs/dutil/WixToolset.DUtil/metautil.cpp
new file mode 100644
index 00000000..f313fc1c
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/metautil.cpp
@@ -0,0 +1,378 @@
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// okay, this may look a little weird, but metautil.h cannot be in the
6// pre-compiled header because we need to #define these things so the
7// correct GUID's get pulled into this object file
8#include <initguid.h>
9#include "metautil.h"
10
11
12// Exit macros
13#define MetaExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_METAUTIL, x, s, __VA_ARGS__)
14#define MetaExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_METAUTIL, x, s, __VA_ARGS__)
15#define MetaExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_METAUTIL, x, s, __VA_ARGS__)
16#define MetaExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_METAUTIL, x, s, __VA_ARGS__)
17#define MetaExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_METAUTIL, x, s, __VA_ARGS__)
18#define MetaExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_METAUTIL, x, s, __VA_ARGS__)
19#define MetaExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_METAUTIL, p, x, e, s, __VA_ARGS__)
20#define MetaExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_METAUTIL, p, x, s, __VA_ARGS__)
21#define MetaExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_METAUTIL, p, x, e, s, __VA_ARGS__)
22#define MetaExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_METAUTIL, p, x, s, __VA_ARGS__)
23#define MetaExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_METAUTIL, e, x, s, __VA_ARGS__)
24#define MetaExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_METAUTIL, g, x, s, __VA_ARGS__)
25
26
27// prototypes
28static void Sort(
29 __in_ecount(cArray) DWORD dwArray[],
30 __in int cArray
31 );
32
33
34/********************************************************************
35 MetaFindWebBase - finds a metabase base string that matches IP, Port and Header
36
37********************************************************************/
38extern "C" HRESULT DAPI MetaFindWebBase(
39 __in IMSAdminBaseW* piMetabase,
40 __in_z LPCWSTR wzIP,
41 __in int iPort,
42 __in_z LPCWSTR wzHeader,
43 __in BOOL fSecure,
44 __out_ecount(cchWebBase) LPWSTR wzWebBase,
45 __in DWORD cchWebBase
46 )
47{
48 Assert(piMetabase && cchWebBase);
49
50 HRESULT hr = S_OK;
51
52 BOOL fFound = FALSE;
53
54 WCHAR wzKey[METADATA_MAX_NAME_LEN];
55 WCHAR wzSubkey[METADATA_MAX_NAME_LEN];
56 DWORD dwIndex = 0;
57
58 METADATA_RECORD mr;
59 METADATA_RECORD mrAddress;
60
61 LPWSTR pwzExists = NULL;
62 LPWSTR pwzIPExists = NULL;
63 LPWSTR pwzPortExists = NULL;
64 int iPortExists = 0;
65 LPCWSTR pwzHeaderExists = NULL;
66
67 memset(&mr, 0, sizeof(mr));
68 mr.dwMDIdentifier = MD_KEY_TYPE;
69 mr.dwMDAttributes = METADATA_INHERIT;
70 mr.dwMDUserType = IIS_MD_UT_SERVER;
71 mr.dwMDDataType = ALL_METADATA;
72
73 memset(&mrAddress, 0, sizeof(mrAddress));
74 mrAddress.dwMDIdentifier = (fSecure) ? MD_SECURE_BINDINGS : MD_SERVER_BINDINGS;
75 mrAddress.dwMDAttributes = METADATA_INHERIT;
76 mrAddress.dwMDUserType = IIS_MD_UT_SERVER;
77 mrAddress.dwMDDataType = ALL_METADATA;
78
79 // loop through the "web keys" looking for the "IIsWebServer" key that matches wzWeb
80 for (dwIndex = 0; SUCCEEDED(hr); ++dwIndex)
81 {
82 hr = piMetabase->EnumKeys(METADATA_MASTER_ROOT_HANDLE, L"/LM/W3SVC", wzSubkey, dwIndex);
83 if (FAILED(hr))
84 break;
85
86 ::StringCchPrintfW(wzKey, countof(wzKey), L"/LM/W3SVC/%s", wzSubkey);
87 hr = MetaGetValue(piMetabase, METADATA_MASTER_ROOT_HANDLE, wzKey, &mr);
88 if (MD_ERROR_DATA_NOT_FOUND == hr || HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr)
89 {
90 hr = S_FALSE; // didn't find anything, try next one
91 continue;
92 }
93 MetaExitOnFailure(hr, "failed to get key from metabase while searching for web servers");
94
95 // if we have an IIsWebServer store the key
96 if (0 == lstrcmpW(L"IIsWebServer", (LPCWSTR)mr.pbMDData))
97 {
98 hr = MetaGetValue(piMetabase, METADATA_MASTER_ROOT_HANDLE, wzKey, &mrAddress);
99 if (MD_ERROR_DATA_NOT_FOUND == hr)
100 hr = S_FALSE;
101 MetaExitOnFailure(hr, "failed to get address from metabase while searching for web servers");
102
103 // break down the first address into parts
104 pwzIPExists = reinterpret_cast<LPWSTR>(mrAddress.pbMDData);
105 pwzExists = wcsstr(pwzIPExists, L":");
106 if (NULL == pwzExists)
107 continue;
108
109 *pwzExists = L'\0';
110
111 pwzPortExists = pwzExists + 1;
112 pwzExists = wcsstr(pwzPortExists, L":");
113 if (NULL == pwzExists)
114 continue;
115
116 *pwzExists = L'\0';
117 iPortExists = wcstol(pwzPortExists, NULL, 10);
118
119 pwzHeaderExists = pwzExists + 1;
120
121 // compare the passed in address with the address listed for this web
122 if (S_OK == hr &&
123 (0 == lstrcmpW(wzIP, pwzIPExists) || 0 == lstrcmpW(wzIP, L"*")) &&
124 iPort == iPortExists &&
125 0 == lstrcmpW(wzHeader, pwzHeaderExists))
126 {
127 // if the passed in buffer wasn't big enough
128 hr = ::StringCchCopyW(wzWebBase, cchWebBase, wzKey);
129 MetaExitOnFailure(hr, "failed to copy in web base: %ls", wzKey);
130
131 fFound = TRUE;
132 break;
133 }
134 }
135 }
136
137 if (E_NOMOREITEMS == hr)
138 {
139 Assert(!fFound);
140 hr = S_FALSE;
141 }
142
143LExit:
144 MetaFreeValue(&mrAddress);
145 MetaFreeValue(&mr);
146
147 if (!fFound && SUCCEEDED(hr))
148 hr = S_FALSE;
149
150 return hr;
151}
152
153
154/********************************************************************
155 MetaFindFreeWebBase - finds the next metabase base string
156
157********************************************************************/
158extern "C" HRESULT DAPI MetaFindFreeWebBase(
159 __in IMSAdminBaseW* piMetabase,
160 __out_ecount(cchWebBase) LPWSTR wzWebBase,
161 __in DWORD cchWebBase
162 )
163{
164 Assert(piMetabase);
165
166 HRESULT hr = S_OK;
167
168 WCHAR wzKey[METADATA_MAX_NAME_LEN];
169 WCHAR wzSubkey[METADATA_MAX_NAME_LEN];
170 DWORD dwSubKeys[100];
171 int cSubKeys = 0;
172 DWORD dwIndex = 0;
173
174 int i;
175 DWORD dwKey;
176
177 METADATA_RECORD mr;
178
179 memset(&mr, 0, sizeof(mr));
180 mr.dwMDIdentifier = MD_KEY_TYPE;
181 mr.dwMDAttributes = 0;
182 mr.dwMDUserType = IIS_MD_UT_SERVER;
183 mr.dwMDDataType = STRING_METADATA;
184
185 // loop through the "web keys" looking for the "IIsWebServer" key that matches wzWeb
186 for (dwIndex = 0; SUCCEEDED(hr); ++dwIndex)
187 {
188 hr = piMetabase->EnumKeys(METADATA_MASTER_ROOT_HANDLE, L"/LM/W3SVC", wzSubkey, dwIndex);
189 if (FAILED(hr))
190 break;
191
192 ::StringCchPrintfW(wzKey, countof(wzKey), L"/LM/W3SVC/%s", wzSubkey);
193
194 hr = MetaGetValue(piMetabase, METADATA_MASTER_ROOT_HANDLE, wzKey, &mr);
195 if (MD_ERROR_DATA_NOT_FOUND == hr || HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr)
196 {
197 hr = S_FALSE; // didn't find anything, try next one
198 continue;
199 }
200 MetaExitOnFailure(hr, "failed to get key from metabase while searching for free web root");
201
202 // if we have a IIsWebServer get the address information
203 if (0 == lstrcmpW(L"IIsWebServer", reinterpret_cast<LPCWSTR>(mr.pbMDData)))
204 {
205 if (cSubKeys >= countof(dwSubKeys))
206 {
207 hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
208 MetaExitOnFailure(hr, "Insufficient buffer to track all sub-WebSites");
209 }
210
211 dwSubKeys[cSubKeys] = wcstol(wzSubkey, NULL, 10);
212 ++cSubKeys;
213 Sort(dwSubKeys, cSubKeys);
214 }
215 }
216
217 if (E_NOMOREITEMS == hr)
218 hr = S_OK;
219 MetaExitOnFailure(hr, "failed to find free web root");
220
221 // find the lowest free web root
222 dwKey = 1;
223 for (i = 0; i < cSubKeys; ++i)
224 {
225 if (dwKey < dwSubKeys[i])
226 break;
227
228 dwKey = dwSubKeys[i] + 1;
229 }
230
231 hr = ::StringCchPrintfW(wzWebBase, cchWebBase, L"/LM/W3SVC/%u", dwKey);
232LExit:
233 MetaFreeValue(&mr);
234 return hr;
235}
236
237
238/********************************************************************
239 MetaOpenKey - open key
240
241********************************************************************/
242extern "C" HRESULT DAPI MetaOpenKey(
243 __in IMSAdminBaseW* piMetabase,
244 __in METADATA_HANDLE mhKey,
245 __in_z LPCWSTR wzKey,
246 __in DWORD dwAccess,
247 __in DWORD cRetries,
248 __out METADATA_HANDLE* pmh
249 )
250{
251 Assert(piMetabase && pmh);
252
253 HRESULT hr = S_OK;
254
255 // loop while the key is busy
256 do
257 {
258 hr = piMetabase->OpenKey(mhKey, wzKey, dwAccess, 10, pmh);
259 if (HRESULT_FROM_WIN32(ERROR_PATH_BUSY) == hr)
260 ::SleepEx(1000, TRUE);
261 } while (HRESULT_FROM_WIN32(ERROR_PATH_BUSY) == hr && 0 < cRetries--);
262
263 return hr;
264}
265
266
267/********************************************************************
268 MetaGetValue - finds the next metabase base string
269
270 NOTE: piMetabase is optional
271********************************************************************/
272extern "C" HRESULT DAPI MetaGetValue(
273 __in IMSAdminBaseW* piMetabase,
274 __in METADATA_HANDLE mhKey,
275 __in_z LPCWSTR wzKey,
276 __inout METADATA_RECORD* pmr
277 )
278{
279 Assert(pmr);
280
281 HRESULT hr = S_OK;
282 BOOL fInitialized = FALSE;
283 DWORD cbRequired = 0;
284
285 if (!piMetabase)
286 {
287 hr = ::CoInitialize(NULL);
288 MetaExitOnFailure(hr, "failed to initialize COM");
289 fInitialized = TRUE;
290
291 hr = ::CoCreateInstance(CLSID_MSAdminBase, NULL, CLSCTX_ALL, IID_IMSAdminBase, reinterpret_cast<LPVOID*>(&piMetabase));
292 MetaExitOnFailure(hr, "failed to get IID_IMSAdminBaseW object");
293 }
294
295 if (!pmr->pbMDData)
296 {
297 pmr->dwMDDataLen = 256;
298 pmr->pbMDData = static_cast<BYTE*>(MemAlloc(pmr->dwMDDataLen, TRUE));
299 MetaExitOnNull(pmr->pbMDData, hr, E_OUTOFMEMORY, "failed to allocate memory for metabase value");
300 }
301 else // set the size of the data to the actual size of the memory
302 {
303 SIZE_T cb = MemSize(pmr->pbMDData);
304 if (cb > DWORD_MAX)
305 {
306 MetaExitOnRootFailure(hr = E_INVALIDSTATE, "metabase data is too large: %Iu", cb);
307 }
308 pmr->dwMDDataLen = (DWORD)cb;
309 }
310
311 hr = piMetabase->GetData(mhKey, wzKey, pmr, &cbRequired);
312 if (HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) == hr)
313 {
314 pmr->dwMDDataLen = cbRequired;
315 BYTE* pb = static_cast<BYTE*>(MemReAlloc(pmr->pbMDData, pmr->dwMDDataLen, TRUE));
316 MetaExitOnNull(pb, hr, E_OUTOFMEMORY, "failed to reallocate memory for metabase value");
317
318 pmr->pbMDData = pb;
319 hr = piMetabase->GetData(mhKey, wzKey, pmr, &cbRequired);
320 }
321 MetaExitOnFailure(hr, "failed to get metabase data");
322
323LExit:
324 if (fInitialized)
325 {
326 ReleaseObject(piMetabase);
327 ::CoUninitialize();
328 }
329
330 return hr;
331}
332
333
334/********************************************************************
335 MetaFreeValue - frees data in METADATA_RECORD remove MetaGetValue()
336
337 NOTE: METADATA_RECORD must have been returned from MetaGetValue() above
338********************************************************************/
339extern "C" void DAPI MetaFreeValue(
340 __in METADATA_RECORD* pmr
341 )
342{
343 Assert(pmr);
344
345 ReleaseNullMem(pmr->pbMDData);
346}
347
348
349//
350// private
351//
352
353/********************************************************************
354 Sort - quick and dirty insertion sort
355
356********************************************************************/
357static void Sort(
358 __in_ecount(cArray) DWORD dwArray[],
359 __in int cArray
360 )
361{
362 int i, j;
363 DWORD dwData;
364
365 for (i = 1; i < cArray; ++i)
366 {
367 dwData = dwArray[i];
368
369 j = i - 1;
370 while (0 <= j && dwArray[j] > dwData)
371 {
372 dwArray[j + 1] = dwArray[j];
373 j--;
374 }
375
376 dwArray[j + 1] = dwData;
377 }
378}
diff --git a/src/libs/dutil/WixToolset.DUtil/monutil.cpp b/src/libs/dutil/WixToolset.DUtil/monutil.cpp
new file mode 100644
index 00000000..6a7f0596
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/monutil.cpp
@@ -0,0 +1,2019 @@
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// Exit macros
7#define MonExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_MONUTIL, x, s, __VA_ARGS__)
8#define MonExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_MONUTIL, x, s, __VA_ARGS__)
9#define MonExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_MONUTIL, x, s, __VA_ARGS__)
10#define MonExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_MONUTIL, x, s, __VA_ARGS__)
11#define MonExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_MONUTIL, x, s, __VA_ARGS__)
12#define MonExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_MONUTIL, x, s, __VA_ARGS__)
13#define MonExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_MONUTIL, p, x, e, s, __VA_ARGS__)
14#define MonExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_MONUTIL, p, x, s, __VA_ARGS__)
15#define MonExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_MONUTIL, p, x, e, s, __VA_ARGS__)
16#define MonExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_MONUTIL, p, x, s, __VA_ARGS__)
17#define MonExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_MONUTIL, e, x, s, __VA_ARGS__)
18#define MonExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_MONUTIL, g, x, s, __VA_ARGS__)
19
20const int MON_THREAD_GROWTH = 5;
21const int MON_ARRAY_GROWTH = 40;
22const int MON_MAX_MONITORS_PER_THREAD = 63;
23const int MON_THREAD_INIT_RETRIES = 1000;
24const int MON_THREAD_INIT_RETRY_PERIOD_IN_MS = 10;
25const int MON_THREAD_NETWORK_FAIL_RETRY_IN_MS = 1000*60; // if we know we failed to connect, retry every minute
26const int MON_THREAD_NETWORK_SUCCESSFUL_RETRY_IN_MS = 1000*60*20; // if we're just checking for remote servers dieing, check much less frequently
27const int MON_THREAD_WAIT_REMOVE_DEVICE = 5000;
28const LPCWSTR MONUTIL_WINDOW_CLASS = L"MonUtilClass";
29
30enum MON_MESSAGE
31{
32 MON_MESSAGE_ADD = WM_APP + 1,
33 MON_MESSAGE_REMOVE,
34 MON_MESSAGE_REMOVED, // Sent by waiter thread back to coordinator thread to indicate a remove occurred
35 MON_MESSAGE_NETWORK_WAIT_FAILED, // Sent by waiter thread back to coordinator thread to indicate a network wait failed. Coordinator thread will periodically trigger retries (via MON_MESSAGE_NETWORK_STATUS_UPDATE messages).
36 MON_MESSAGE_NETWORK_WAIT_SUCCEEDED, // Sent by waiter thread back to coordinator thread to indicate a previously failing network wait is now succeeding. Coordinator thread will stop triggering retries if no other failing waits exist.
37 MON_MESSAGE_NETWORK_STATUS_UPDATE, // Some change to network connectivity occurred (a network connection was connected or disconnected for example)
38 MON_MESSAGE_NETWORK_RETRY_SUCCESSFUL_NETWORK_WAITS, // Coordinator thread is telling waiters to retry any successful network waits.
39 // Annoyingly, this is necessary to catch the rare case that the remote server goes offline unexpectedly, such as by
40 // network cable unplugged or power loss - in this case there is no local network status change, and the wait will just never fire.
41 // So we very occasionally retry all successful network waits. When this occurs, we notify for changes, even though there may not have been any.
42 // This is because we have no way to detect if the old wait had failed (and changes were lost) due to the remote server going offline during that time or not.
43 // If we do this often, it can cause a lot of wasted work (which could be expensive for battery life), so the default is to do it very rarely (every 20 minutes).
44 MON_MESSAGE_NETWORK_RETRY_FAILED_NETWORK_WAITS, // Coordinator thread is telling waiters to retry any failed network waits
45 MON_MESSAGE_DRIVE_STATUS_UPDATE, // Some change to local drive has occurred (new drive created or plugged in, or removed)
46 MON_MESSAGE_DRIVE_QUERY_REMOVE, // User wants to unplug a drive, which MonUtil will always allow
47 MON_MESSAGE_STOP
48};
49
50enum MON_TYPE
51{
52 MON_NONE = 0,
53 MON_DIRECTORY = 1,
54 MON_REGKEY = 2
55};
56
57struct MON_REQUEST
58{
59 MON_TYPE type;
60 DWORD dwMaxSilencePeriodInMs;
61
62 // Handle to the main window for RegisterDeviceNotification() (same handle as owned by coordinator thread)
63 HWND hwnd;
64 // and handle to the notification (specific to this request)
65 HDEVNOTIFY hNotify;
66
67 BOOL fRecursive;
68 void *pvContext;
69
70 HRESULT hrStatus;
71
72 LPWSTR sczOriginalPathRequest;
73 BOOL fNetwork; // This reflects either a UNC or mounted drive original request
74 DWORD dwPathHierarchyIndex;
75 LPWSTR *rgsczPathHierarchy;
76 DWORD cPathHierarchy;
77
78 // If the notify fires, fPendingFire gets set to TRUE, and we wait to see if other writes are occurring, and only after the configured silence period do we notify of changes
79 // after notification, we set fPendingFire back to FALSE
80 BOOL fPendingFire;
81 BOOL fSkipDeltaAdd;
82 DWORD dwSilencePeriodInMs;
83
84 union
85 {
86 struct
87 {
88 } directory;
89 struct
90 {
91 HKEY hkRoot;
92 HKEY hkSubKey;
93 REG_KEY_BITNESS kbKeyBitness; // Only used to pass on 32-bit, 64-bit, or default parameter
94 } regkey;
95 };
96};
97
98struct MON_ADD_MESSAGE
99{
100 MON_REQUEST request;
101 HANDLE handle;
102};
103
104struct MON_REMOVE_MESSAGE
105{
106 MON_TYPE type;
107 BOOL fRecursive;
108
109 union
110 {
111 struct
112 {
113 LPWSTR sczDirectory;
114 } directory;
115 struct
116 {
117 HKEY hkRoot;
118 LPWSTR sczSubKey;
119 REG_KEY_BITNESS kbKeyBitness;
120 } regkey;
121 };
122};
123
124struct MON_WAITER_CONTEXT
125{
126 DWORD dwCoordinatorThreadId;
127
128 HANDLE hWaiterThread;
129 DWORD dwWaiterThreadId;
130 BOOL fWaiterThreadMessageQueueInitialized;
131
132 // Callbacks
133 PFN_MONGENERAL vpfMonGeneral;
134 PFN_MONDIRECTORY vpfMonDirectory;
135 PFN_MONREGKEY vpfMonRegKey;
136
137 // Context for callbacks
138 LPVOID pvContext;
139
140 // HANDLEs are in their own array for easy use with WaitForMultipleObjects()
141 // After initialization, the very first handle is just to wake the listener thread to have it re-wait on a new list
142 // Because this array is read by both coordinator thread and waiter thread, to avoid locking between both threads, it must start at the maximum size
143 HANDLE *rgHandles;
144 DWORD cHandles;
145
146 // Requested things to monitor
147 MON_REQUEST *rgRequests;
148 DWORD cRequests;
149
150 // Number of pending notifications
151 DWORD cRequestsPending;
152
153 // Number of requests in a failed state (couldn't initiate wait)
154 DWORD cRequestsFailing;
155};
156
157// Info stored about each waiter by the coordinator
158struct MON_WAITER_INFO
159{
160 DWORD cMonitorCount;
161
162 MON_WAITER_CONTEXT *pWaiterContext;
163};
164
165// This struct is used when Thread A wants to send a task to another thread B (and get notified when the task finishes)
166// You typically declare this struct in a manner that a pointer to it is valid as long as a thread that could respond is still running
167// (even long after sender is no longer waiting, in case thread has huge message queue)
168// and you must send 2 parameters in the message:
169// 1) a pointer to this struct (which is always valid)
170// 2) the original value of dwIteration
171// The receiver of the message can compare the current value of dwSendIteration in the struct with what was sent in the message
172// If values are different, we're too late and thread A is no longer waiting on this response
173// otherwise, set dwResponseIteration to the same value, and call ::SetEvent() on hWait
174// Thread A will then wakeup, and must verify that dwResponseIteration == dwSendIteration to ensure it isn't an earlier out-of-date reply
175// replying to a newer wait
176// pvContext is used to send a misc parameter related to processing data
177struct MON_INTERNAL_TEMPORARY_WAIT
178{
179 // Should be incremented each time sender sends a pointer to this struct, so each request has a different iteration
180 DWORD dwSendIteration;
181 DWORD dwReceiveIteration;
182 HANDLE hWait;
183 void *pvContext;
184};
185
186struct MON_STRUCT
187{
188 HANDLE hCoordinatorThread;
189 DWORD dwCoordinatorThreadId;
190 BOOL fCoordinatorThreadMessageQueueInitialized;
191
192 // Invisible window for receiving network status & drive added/removal messages
193 HWND hwnd;
194 // Used by window procedure for sending request and waiting for response from waiter threads
195 // such as in event of a request to remove a device
196 MON_INTERNAL_TEMPORARY_WAIT internalWait;
197
198 // Callbacks
199 PFN_MONGENERAL vpfMonGeneral;
200 PFN_MONDRIVESTATUS vpfMonDriveStatus;
201 PFN_MONDIRECTORY vpfMonDirectory;
202 PFN_MONREGKEY vpfMonRegKey;
203
204 // Context for callbacks
205 LPVOID pvContext;
206
207 // Waiter thread array
208 MON_WAITER_INFO *rgWaiterThreads;
209 DWORD cWaiterThreads;
210};
211
212const int MON_HANDLE_BYTES = sizeof(MON_STRUCT);
213
214static DWORD WINAPI CoordinatorThread(
215 __in_bcount(sizeof(MON_STRUCT)) LPVOID pvContext
216 );
217// Initiates (or if *pHandle is non-null, continues) wait on the directory or subkey
218// if the directory or subkey doesn't exist, instead calls it on the first existing parent directory or subkey
219// writes to pRequest->dwPathHierarchyIndex with the array index that was waited on
220static HRESULT InitiateWait(
221 __inout MON_REQUEST *pRequest,
222 __inout HANDLE *pHandle
223 );
224static DWORD WINAPI WaiterThread(
225 __in_bcount(sizeof(MON_WAITER_CONTEXT)) LPVOID pvContext
226 );
227static void Notify(
228 __in HRESULT hr,
229 __in MON_WAITER_CONTEXT *pWaiterContext,
230 __in MON_REQUEST *pRequest
231 );
232static void MonRequestDestroy(
233 __in MON_REQUEST *pRequest
234 );
235static void MonAddMessageDestroy(
236 __in_opt MON_ADD_MESSAGE *pMessage
237 );
238static void MonRemoveMessageDestroy(
239 __in_opt MON_REMOVE_MESSAGE *pMessage
240 );
241static BOOL GetRecursiveFlag(
242 __in MON_REQUEST *pRequest,
243 __in DWORD dwIndex
244 );
245static HRESULT FindRequestIndex(
246 __in MON_WAITER_CONTEXT *pWaiterContext,
247 __in MON_REMOVE_MESSAGE *pMessage,
248 __out DWORD *pdwIndex
249 );
250static HRESULT RemoveRequest(
251 __inout MON_WAITER_CONTEXT *pWaiterContext,
252 __in DWORD dwRequestIndex
253 );
254static REGSAM GetRegKeyBitness(
255 __in MON_REQUEST *pRequest
256 );
257static HRESULT DuplicateRemoveMessage(
258 __in MON_REMOVE_MESSAGE *pMessage,
259 __out MON_REMOVE_MESSAGE **ppMessage
260 );
261static LRESULT CALLBACK MonWndProc(
262 __in HWND hWnd,
263 __in UINT uMsg,
264 __in WPARAM wParam,
265 __in LPARAM lParam
266 );
267static HRESULT CreateMonWindow(
268 __in MON_STRUCT *pm,
269 __out HWND *pHwnd
270 );
271// if *phMonitor is non-NULL, closes the old wait before re-starting the new wait
272static HRESULT WaitForNetworkChanges(
273 __inout HANDLE *phMonitor,
274 __in MON_STRUCT *pm
275 );
276static HRESULT UpdateWaitStatus(
277 __in HRESULT hrNewStatus,
278 __inout MON_WAITER_CONTEXT *pWaiterContext,
279 __in DWORD dwRequestIndex,
280 __out_opt DWORD *pdwNewRequestIndex
281 );
282
283extern "C" HRESULT DAPI MonCreate(
284 __out_bcount(MON_HANDLE_BYTES) MON_HANDLE *pHandle,
285 __in PFN_MONGENERAL vpfMonGeneral,
286 __in_opt PFN_MONDRIVESTATUS vpfMonDriveStatus,
287 __in_opt PFN_MONDIRECTORY vpfMonDirectory,
288 __in_opt PFN_MONREGKEY vpfMonRegKey,
289 __in_opt LPVOID pvContext
290 )
291{
292 HRESULT hr = S_OK;
293 DWORD dwRetries = MON_THREAD_INIT_RETRIES;
294
295 MonExitOnNull(pHandle, hr, E_INVALIDARG, "Pointer to handle not specified while creating monitor");
296
297 // Allocate the struct
298 *pHandle = static_cast<MON_HANDLE>(MemAlloc(sizeof(MON_STRUCT), TRUE));
299 MonExitOnNull(*pHandle, hr, E_OUTOFMEMORY, "Failed to allocate monitor object");
300
301 MON_STRUCT *pm = static_cast<MON_STRUCT *>(*pHandle);
302
303 pm->vpfMonGeneral = vpfMonGeneral;
304 pm->vpfMonDriveStatus = vpfMonDriveStatus;
305 pm->vpfMonDirectory = vpfMonDirectory;
306 pm->vpfMonRegKey = vpfMonRegKey;
307 pm->pvContext = pvContext;
308
309 pm->hCoordinatorThread = ::CreateThread(NULL, 0, CoordinatorThread, pm, 0, &pm->dwCoordinatorThreadId);
310 if (!pm->hCoordinatorThread)
311 {
312 MonExitWithLastError(hr, "Failed to create waiter thread.");
313 }
314
315 // Ensure the created thread initializes its message queue. It does this first thing, so if it doesn't within 10 seconds, there must be a huge problem.
316 while (!pm->fCoordinatorThreadMessageQueueInitialized && 0 < dwRetries)
317 {
318 ::Sleep(MON_THREAD_INIT_RETRY_PERIOD_IN_MS);
319 --dwRetries;
320 }
321
322 if (0 == dwRetries)
323 {
324 hr = E_UNEXPECTED;
325 MonExitOnFailure(hr, "Waiter thread apparently never initialized its message queue.");
326 }
327
328LExit:
329 return hr;
330}
331
332extern "C" HRESULT DAPI MonAddDirectory(
333 __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle,
334 __in_z LPCWSTR wzDirectory,
335 __in BOOL fRecursive,
336 __in DWORD dwSilencePeriodInMs,
337 __in_opt LPVOID pvDirectoryContext
338 )
339{
340 HRESULT hr = S_OK;
341 MON_STRUCT *pm = static_cast<MON_STRUCT *>(handle);
342 LPWSTR sczDirectory = NULL;
343 LPWSTR sczOriginalPathRequest = NULL;
344 MON_ADD_MESSAGE *pMessage = NULL;
345
346 hr = StrAllocString(&sczOriginalPathRequest, wzDirectory, 0);
347 MonExitOnFailure(hr, "Failed to convert directory string to UNC path");
348
349 hr = PathBackslashTerminate(&sczOriginalPathRequest);
350 MonExitOnFailure(hr, "Failed to ensure directory ends in backslash");
351
352 pMessage = reinterpret_cast<MON_ADD_MESSAGE *>(MemAlloc(sizeof(MON_ADD_MESSAGE), TRUE));
353 MonExitOnNull(pMessage, hr, E_OUTOFMEMORY, "Failed to allocate memory for message");
354
355 if (sczOriginalPathRequest[0] == L'\\' && sczOriginalPathRequest[1] == L'\\')
356 {
357 pMessage->request.fNetwork = TRUE;
358 }
359 else
360 {
361 hr = UncConvertFromMountedDrive(&sczDirectory, sczOriginalPathRequest);
362 if (SUCCEEDED(hr))
363 {
364 pMessage->request.fNetwork = TRUE;
365 }
366 }
367
368 if (NULL == sczDirectory)
369 {
370 // Likely not a mounted drive - just copy the request then
371 hr = S_OK;
372
373 hr = StrAllocString(&sczDirectory, sczOriginalPathRequest, 0);
374 MonExitOnFailure(hr, "Failed to copy original path request: %ls", sczOriginalPathRequest);
375 }
376
377 pMessage->handle = INVALID_HANDLE_VALUE;
378 pMessage->request.type = MON_DIRECTORY;
379 pMessage->request.fRecursive = fRecursive;
380 pMessage->request.dwMaxSilencePeriodInMs = dwSilencePeriodInMs;
381 pMessage->request.hwnd = pm->hwnd;
382 pMessage->request.pvContext = pvDirectoryContext;
383 pMessage->request.sczOriginalPathRequest = sczOriginalPathRequest;
384 sczOriginalPathRequest = NULL;
385
386 hr = PathGetHierarchyArray(sczDirectory, &pMessage->request.rgsczPathHierarchy, reinterpret_cast<LPUINT>(&pMessage->request.cPathHierarchy));
387 MonExitOnFailure(hr, "Failed to get hierarchy array for path %ls", sczDirectory);
388
389 if (0 < pMessage->request.cPathHierarchy)
390 {
391 pMessage->request.hrStatus = InitiateWait(&pMessage->request, &pMessage->handle);
392 if (!::PostThreadMessageW(pm->dwCoordinatorThreadId, MON_MESSAGE_ADD, reinterpret_cast<WPARAM>(pMessage), 0))
393 {
394 MonExitWithLastError(hr, "Failed to send message to worker thread to add directory wait for path %ls", sczDirectory);
395 }
396 pMessage = NULL;
397 }
398
399LExit:
400 ReleaseStr(sczDirectory);
401 ReleaseStr(sczOriginalPathRequest);
402 MonAddMessageDestroy(pMessage);
403
404 return hr;
405}
406
407extern "C" HRESULT DAPI MonAddRegKey(
408 __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle,
409 __in HKEY hkRoot,
410 __in_z LPCWSTR wzSubKey,
411 __in REG_KEY_BITNESS kbKeyBitness,
412 __in BOOL fRecursive,
413 __in DWORD dwSilencePeriodInMs,
414 __in_opt LPVOID pvRegKeyContext
415 )
416{
417 HRESULT hr = S_OK;
418 MON_STRUCT *pm = static_cast<MON_STRUCT *>(handle);
419 LPWSTR sczSubKey = NULL;
420 MON_ADD_MESSAGE *pMessage = NULL;
421
422 hr = StrAllocString(&sczSubKey, wzSubKey, 0);
423 MonExitOnFailure(hr, "Failed to copy subkey string");
424
425 hr = PathBackslashTerminate(&sczSubKey);
426 MonExitOnFailure(hr, "Failed to ensure subkey path ends in backslash");
427
428 pMessage = reinterpret_cast<MON_ADD_MESSAGE *>(MemAlloc(sizeof(MON_ADD_MESSAGE), TRUE));
429 MonExitOnNull(pMessage, hr, E_OUTOFMEMORY, "Failed to allocate memory for message");
430
431 pMessage->handle = ::CreateEventW(NULL, TRUE, FALSE, NULL);
432 MonExitOnNullWithLastError(pMessage->handle, hr, "Failed to create anonymous event for regkey monitor");
433
434 pMessage->request.type = MON_REGKEY;
435 pMessage->request.regkey.hkRoot = hkRoot;
436 pMessage->request.regkey.kbKeyBitness = kbKeyBitness;
437 pMessage->request.fRecursive = fRecursive;
438 pMessage->request.dwMaxSilencePeriodInMs = dwSilencePeriodInMs,
439 pMessage->request.hwnd = pm->hwnd;
440 pMessage->request.pvContext = pvRegKeyContext;
441
442 hr = PathGetHierarchyArray(sczSubKey, &pMessage->request.rgsczPathHierarchy, reinterpret_cast<LPUINT>(&pMessage->request.cPathHierarchy));
443 MonExitOnFailure(hr, "Failed to get hierarchy array for subkey %ls", sczSubKey);
444
445 if (0 < pMessage->request.cPathHierarchy)
446 {
447 pMessage->request.hrStatus = InitiateWait(&pMessage->request, &pMessage->handle);
448 MonExitOnFailure(hr, "Failed to initiate wait");
449
450 if (!::PostThreadMessageW(pm->dwCoordinatorThreadId, MON_MESSAGE_ADD, reinterpret_cast<WPARAM>(pMessage), 0))
451 {
452 MonExitWithLastError(hr, "Failed to send message to worker thread to add directory wait for regkey %ls", sczSubKey);
453 }
454 pMessage = NULL;
455 }
456
457LExit:
458 ReleaseStr(sczSubKey);
459 MonAddMessageDestroy(pMessage);
460
461 return hr;
462}
463
464extern "C" HRESULT DAPI MonRemoveDirectory(
465 __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle,
466 __in_z LPCWSTR wzDirectory,
467 __in BOOL fRecursive
468 )
469{
470 HRESULT hr = S_OK;
471 MON_STRUCT *pm = static_cast<MON_STRUCT *>(handle);
472 LPWSTR sczDirectory = NULL;
473 MON_REMOVE_MESSAGE *pMessage = NULL;
474
475 hr = StrAllocString(&sczDirectory, wzDirectory, 0);
476 MonExitOnFailure(hr, "Failed to copy directory string");
477
478 hr = PathBackslashTerminate(&sczDirectory);
479 MonExitOnFailure(hr, "Failed to ensure directory ends in backslash");
480
481 pMessage = reinterpret_cast<MON_REMOVE_MESSAGE *>(MemAlloc(sizeof(MON_REMOVE_MESSAGE), TRUE));
482 MonExitOnNull(pMessage, hr, E_OUTOFMEMORY, "Failed to allocate memory for message");
483
484 pMessage->type = MON_DIRECTORY;
485 pMessage->fRecursive = fRecursive;
486
487 hr = StrAllocString(&pMessage->directory.sczDirectory, sczDirectory, 0);
488 MonExitOnFailure(hr, "Failed to allocate copy of directory string");
489
490 if (!::PostThreadMessageW(pm->dwCoordinatorThreadId, MON_MESSAGE_REMOVE, reinterpret_cast<WPARAM>(pMessage), 0))
491 {
492 MonExitWithLastError(hr, "Failed to send message to worker thread to add directory wait for path %ls", sczDirectory);
493 }
494 pMessage = NULL;
495
496LExit:
497 MonRemoveMessageDestroy(pMessage);
498
499 return hr;
500}
501
502extern "C" HRESULT DAPI MonRemoveRegKey(
503 __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle,
504 __in HKEY hkRoot,
505 __in_z LPCWSTR wzSubKey,
506 __in REG_KEY_BITNESS kbKeyBitness,
507 __in BOOL fRecursive
508 )
509{
510 HRESULT hr = S_OK;
511 MON_STRUCT *pm = static_cast<MON_STRUCT *>(handle);
512 LPWSTR sczSubKey = NULL;
513 MON_REMOVE_MESSAGE *pMessage = NULL;
514
515 hr = StrAllocString(&sczSubKey, wzSubKey, 0);
516 MonExitOnFailure(hr, "Failed to copy subkey string");
517
518 hr = PathBackslashTerminate(&sczSubKey);
519 MonExitOnFailure(hr, "Failed to ensure subkey path ends in backslash");
520
521 pMessage = reinterpret_cast<MON_REMOVE_MESSAGE *>(MemAlloc(sizeof(MON_REMOVE_MESSAGE), TRUE));
522 MonExitOnNull(pMessage, hr, E_OUTOFMEMORY, "Failed to allocate memory for message");
523
524 pMessage->type = MON_REGKEY;
525 pMessage->regkey.hkRoot = hkRoot;
526 pMessage->regkey.kbKeyBitness = kbKeyBitness;
527 pMessage->fRecursive = fRecursive;
528
529 hr = StrAllocString(&pMessage->regkey.sczSubKey, sczSubKey, 0);
530 MonExitOnFailure(hr, "Failed to allocate copy of directory string");
531
532 if (!::PostThreadMessageW(pm->dwCoordinatorThreadId, MON_MESSAGE_REMOVE, reinterpret_cast<WPARAM>(pMessage), 0))
533 {
534 MonExitWithLastError(hr, "Failed to send message to worker thread to add directory wait for path %ls", sczSubKey);
535 }
536 pMessage = NULL;
537
538LExit:
539 ReleaseStr(sczSubKey);
540 MonRemoveMessageDestroy(pMessage);
541
542 return hr;
543}
544
545extern "C" void DAPI MonDestroy(
546 __in_bcount(MON_HANDLE_BYTES) MON_HANDLE handle
547 )
548{
549 HRESULT hr = S_OK;
550 DWORD er = ERROR_SUCCESS;
551 MON_STRUCT *pm = static_cast<MON_STRUCT *>(handle);
552
553 if (!::PostThreadMessageW(pm->dwCoordinatorThreadId, MON_MESSAGE_STOP, 0, 0))
554 {
555 er = ::GetLastError();
556 if (ERROR_INVALID_THREAD_ID == er)
557 {
558 // It already halted, or doesn't exist for some other reason, so let's just ignore it and clean up
559 er = ERROR_SUCCESS;
560 }
561 MonExitOnWin32Error(er, hr, "Failed to send message to background thread to halt");
562 }
563
564 if (pm->hCoordinatorThread)
565 {
566 ::WaitForSingleObject(pm->hCoordinatorThread, INFINITE);
567 ::CloseHandle(pm->hCoordinatorThread);
568 }
569
570LExit:
571 return;
572}
573
574static void MonRequestDestroy(
575 __in MON_REQUEST *pRequest
576 )
577{
578 if (NULL != pRequest)
579 {
580 if (MON_REGKEY == pRequest->type)
581 {
582 ReleaseRegKey(pRequest->regkey.hkSubKey);
583 }
584 else if (MON_DIRECTORY == pRequest->type && pRequest->hNotify)
585 {
586 UnregisterDeviceNotification(pRequest->hNotify);
587 pRequest->hNotify = NULL;
588 }
589 ReleaseStr(pRequest->sczOriginalPathRequest);
590 ReleaseStrArray(pRequest->rgsczPathHierarchy, pRequest->cPathHierarchy);
591 }
592}
593
594static void MonAddMessageDestroy(
595 __in_opt MON_ADD_MESSAGE *pMessage
596 )
597{
598 if (pMessage)
599 {
600 MonRequestDestroy(&pMessage->request);
601 if (MON_DIRECTORY == pMessage->request.type && INVALID_HANDLE_VALUE != pMessage->handle)
602 {
603 ::FindCloseChangeNotification(pMessage->handle);
604 }
605 else if (MON_REGKEY == pMessage->request.type)
606 {
607 ReleaseHandle(pMessage->handle);
608 }
609
610 ReleaseMem(pMessage);
611 }
612}
613
614static void MonRemoveMessageDestroy(
615 __in_opt MON_REMOVE_MESSAGE *pMessage
616 )
617{
618 if (pMessage)
619 {
620 switch (pMessage->type)
621 {
622 case MON_DIRECTORY:
623 ReleaseStr(pMessage->directory.sczDirectory);
624 break;
625 case MON_REGKEY:
626 ReleaseStr(pMessage->regkey.sczSubKey);
627 break;
628 default:
629 Assert(false);
630 }
631
632 ReleaseMem(pMessage);
633 }
634}
635
636static DWORD WINAPI CoordinatorThread(
637 __in_bcount(sizeof(MON_STRUCT)) LPVOID pvContext
638 )
639{
640 HRESULT hr = S_OK;
641 MSG msg = { };
642 DWORD dwThreadIndex = DWORD_MAX;
643 DWORD dwRetries;
644 DWORD dwFailingNetworkWaits = 0;
645 MON_WAITER_CONTEXT *pWaiterContext = NULL;
646 MON_REMOVE_MESSAGE *pRemoveMessage = NULL;
647 MON_REMOVE_MESSAGE *pTempRemoveMessage = NULL;
648 MON_STRUCT *pm = reinterpret_cast<MON_STRUCT*>(pvContext);
649 WSADATA wsaData = { };
650 HANDLE hMonitor = NULL;
651 BOOL fRet = FALSE;
652 UINT_PTR uTimerSuccessfulNetworkRetry = 0;
653 UINT_PTR uTimerFailedNetworkRetry = 0;
654
655 // Ensure the thread has a message queue
656 ::PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
657 pm->fCoordinatorThreadMessageQueueInitialized = TRUE;
658
659 hr = CreateMonWindow(pm, &pm->hwnd);
660 MonExitOnFailure(hr, "Failed to create window for status update thread");
661
662 ::WSAStartup(MAKEWORD(2, 2), &wsaData);
663
664 hr = WaitForNetworkChanges(&hMonitor, pm);
665 MonExitOnFailure(hr, "Failed to wait for network changes");
666
667 uTimerSuccessfulNetworkRetry = ::SetTimer(NULL, 1, MON_THREAD_NETWORK_SUCCESSFUL_RETRY_IN_MS, NULL);
668 if (0 == uTimerSuccessfulNetworkRetry)
669 {
670 MonExitWithLastError(hr, "Failed to set timer for network successful retry");
671 }
672
673 while (0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0)))
674 {
675 if (-1 == fRet)
676 {
677 hr = E_UNEXPECTED;
678 MonExitOnRootFailure(hr, "Unexpected return value from message pump.");
679 }
680 else
681 {
682 switch (msg.message)
683 {
684 case MON_MESSAGE_ADD:
685 dwThreadIndex = DWORD_MAX;
686 for (DWORD i = 0; i < pm->cWaiterThreads; ++i)
687 {
688 if (pm->rgWaiterThreads[i].cMonitorCount < MON_MAX_MONITORS_PER_THREAD)
689 {
690 dwThreadIndex = i;
691 break;
692 }
693 }
694
695 if (dwThreadIndex < pm->cWaiterThreads)
696 {
697 pWaiterContext = pm->rgWaiterThreads[dwThreadIndex].pWaiterContext;
698 }
699 else
700 {
701 hr = MemEnsureArraySize(reinterpret_cast<void **>(&pm->rgWaiterThreads), pm->cWaiterThreads + 1, sizeof(MON_WAITER_INFO), MON_THREAD_GROWTH);
702 MonExitOnFailure(hr, "Failed to grow waiter thread array size");
703 ++pm->cWaiterThreads;
704
705 dwThreadIndex = pm->cWaiterThreads - 1;
706 pm->rgWaiterThreads[dwThreadIndex].pWaiterContext = reinterpret_cast<MON_WAITER_CONTEXT*>(MemAlloc(sizeof(MON_WAITER_CONTEXT), TRUE));
707 MonExitOnNull(pm->rgWaiterThreads[dwThreadIndex].pWaiterContext, hr, E_OUTOFMEMORY, "Failed to allocate waiter context struct");
708 pWaiterContext = pm->rgWaiterThreads[dwThreadIndex].pWaiterContext;
709 pWaiterContext->dwCoordinatorThreadId = ::GetCurrentThreadId();
710 pWaiterContext->vpfMonGeneral = pm->vpfMonGeneral;
711 pWaiterContext->vpfMonDirectory = pm->vpfMonDirectory;
712 pWaiterContext->vpfMonRegKey = pm->vpfMonRegKey;
713 pWaiterContext->pvContext = pm->pvContext;
714
715 hr = MemEnsureArraySize(reinterpret_cast<void **>(&pWaiterContext->rgHandles), MON_MAX_MONITORS_PER_THREAD + 1, sizeof(HANDLE), 0);
716 MonExitOnFailure(hr, "Failed to allocate first handle");
717 pWaiterContext->cHandles = 1;
718
719 pWaiterContext->rgHandles[0] = ::CreateEventW(NULL, FALSE, FALSE, NULL);
720 MonExitOnNullWithLastError(pWaiterContext->rgHandles[0], hr, "Failed to create general event");
721
722 pWaiterContext->hWaiterThread = ::CreateThread(NULL, 0, WaiterThread, pWaiterContext, 0, &pWaiterContext->dwWaiterThreadId);
723 if (!pWaiterContext->hWaiterThread)
724 {
725 MonExitWithLastError(hr, "Failed to create waiter thread.");
726 }
727
728 dwRetries = MON_THREAD_INIT_RETRIES;
729 while (!pWaiterContext->fWaiterThreadMessageQueueInitialized && 0 < dwRetries)
730 {
731 ::Sleep(MON_THREAD_INIT_RETRY_PERIOD_IN_MS);
732 --dwRetries;
733 }
734
735 if (0 == dwRetries)
736 {
737 hr = E_UNEXPECTED;
738 MonExitOnFailure(hr, "Waiter thread apparently never initialized its message queue.");
739 }
740 }
741
742 ++pm->rgWaiterThreads[dwThreadIndex].cMonitorCount;
743 if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, MON_MESSAGE_ADD, msg.wParam, 0))
744 {
745 MonExitWithLastError(hr, "Failed to send message to waiter thread to add monitor");
746 }
747
748 if (!::SetEvent(pWaiterContext->rgHandles[0]))
749 {
750 MonExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming message");
751 }
752 break;
753
754 case MON_MESSAGE_REMOVE:
755 // Send remove to all waiter threads. They'll ignore it if they don't have that monitor.
756 // If they do have that monitor, they'll remove it from their list, and tell coordinator they have another
757 // empty slot via MON_MESSAGE_REMOVED message
758 for (DWORD i = 0; i < pm->cWaiterThreads; ++i)
759 {
760 pWaiterContext = pm->rgWaiterThreads[i].pWaiterContext;
761 pRemoveMessage = reinterpret_cast<MON_REMOVE_MESSAGE *>(msg.wParam);
762
763 hr = DuplicateRemoveMessage(pRemoveMessage, &pTempRemoveMessage);
764 MonExitOnFailure(hr, "Failed to duplicate remove message");
765
766 if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, MON_MESSAGE_REMOVE, reinterpret_cast<WPARAM>(pTempRemoveMessage), msg.lParam))
767 {
768 MonExitWithLastError(hr, "Failed to send message to waiter thread to add monitor");
769 }
770 pTempRemoveMessage = NULL;
771
772 if (!::SetEvent(pWaiterContext->rgHandles[0]))
773 {
774 MonExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming remove message");
775 }
776 }
777 MonRemoveMessageDestroy(pRemoveMessage);
778 pRemoveMessage = NULL;
779 break;
780
781 case MON_MESSAGE_REMOVED:
782 for (DWORD i = 0; i < pm->cWaiterThreads; ++i)
783 {
784 if (pm->rgWaiterThreads[i].pWaiterContext->dwWaiterThreadId == static_cast<DWORD>(msg.wParam))
785 {
786 Assert(pm->rgWaiterThreads[i].cMonitorCount > 0);
787 --pm->rgWaiterThreads[i].cMonitorCount;
788 if (0 == pm->rgWaiterThreads[i].cMonitorCount)
789 {
790 if (!::PostThreadMessageW(pm->rgWaiterThreads[i].pWaiterContext->dwWaiterThreadId, MON_MESSAGE_STOP, msg.wParam, msg.lParam))
791 {
792 MonExitWithLastError(hr, "Failed to send message to waiter thread to stop");
793 }
794 MemRemoveFromArray(reinterpret_cast<LPVOID>(pm->rgWaiterThreads), i, 1, pm->cWaiterThreads, sizeof(MON_WAITER_INFO), TRUE);
795 --pm->cWaiterThreads;
796 --i; // reprocess this index in the for loop, which will now contain the item after the one we removed
797 }
798 }
799 }
800 break;
801
802 case MON_MESSAGE_NETWORK_WAIT_FAILED:
803 if (0 == dwFailingNetworkWaits)
804 {
805 uTimerFailedNetworkRetry = ::SetTimer(NULL, uTimerSuccessfulNetworkRetry + 1, MON_THREAD_NETWORK_FAIL_RETRY_IN_MS, NULL);
806 if (0 == uTimerFailedNetworkRetry)
807 {
808 MonExitWithLastError(hr, "Failed to set timer for network fail retry");
809 }
810 }
811 ++dwFailingNetworkWaits;
812 break;
813
814 case MON_MESSAGE_NETWORK_WAIT_SUCCEEDED:
815 --dwFailingNetworkWaits;
816 if (0 == dwFailingNetworkWaits)
817 {
818 if (!::KillTimer(NULL, uTimerFailedNetworkRetry))
819 {
820 MonExitWithLastError(hr, "Failed to kill timer for network fail retry");
821 }
822 uTimerFailedNetworkRetry = 0;
823 }
824 break;
825
826 case MON_MESSAGE_NETWORK_STATUS_UPDATE:
827 hr = WaitForNetworkChanges(&hMonitor, pm);
828 MonExitOnFailure(hr, "Failed to re-wait for network changes");
829
830 // Propagate any network status update messages to all waiter threads
831 for (DWORD i = 0; i < pm->cWaiterThreads; ++i)
832 {
833 pWaiterContext = pm->rgWaiterThreads[i].pWaiterContext;
834
835 if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, MON_MESSAGE_NETWORK_STATUS_UPDATE, 0, 0))
836 {
837 MonExitWithLastError(hr, "Failed to send message to waiter thread to notify of network status update");
838 }
839
840 if (!::SetEvent(pWaiterContext->rgHandles[0]))
841 {
842 MonExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming network status update message");
843 }
844 }
845 break;
846
847 case WM_TIMER:
848 // Timer means some network wait is failing, and we need to retry every so often in case a remote server goes back up
849 for (DWORD i = 0; i < pm->cWaiterThreads; ++i)
850 {
851 pWaiterContext = pm->rgWaiterThreads[i].pWaiterContext;
852
853 if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, msg.wParam == uTimerFailedNetworkRetry ? MON_MESSAGE_NETWORK_RETRY_FAILED_NETWORK_WAITS : MON_MESSAGE_NETWORK_RETRY_SUCCESSFUL_NETWORK_WAITS, 0, 0))
854 {
855 MonExitWithLastError(hr, "Failed to send message to waiter thread to notify of network status update");
856 }
857
858 if (!::SetEvent(pWaiterContext->rgHandles[0]))
859 {
860 MonExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming network status update message");
861 }
862 }
863 break;
864
865 case MON_MESSAGE_DRIVE_STATUS_UPDATE:
866 // If user requested to be notified of drive status updates, notify!
867 if (pm->vpfMonDriveStatus)
868 {
869 pm->vpfMonDriveStatus(static_cast<WCHAR>(msg.wParam), static_cast<BOOL>(msg.lParam), pm->pvContext);
870 }
871
872 // Propagate any drive status update messages to all waiter threads
873 for (DWORD i = 0; i < pm->cWaiterThreads; ++i)
874 {
875 pWaiterContext = pm->rgWaiterThreads[i].pWaiterContext;
876
877 if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, MON_MESSAGE_DRIVE_STATUS_UPDATE, msg.wParam, msg.lParam))
878 {
879 MonExitWithLastError(hr, "Failed to send message to waiter thread to notify of drive status update");
880 }
881
882 if (!::SetEvent(pWaiterContext->rgHandles[0]))
883 {
884 MonExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming drive status update message");
885 }
886 }
887 break;
888
889 case MON_MESSAGE_STOP:
890 ExitFunction1(hr = static_cast<HRESULT>(msg.wParam));
891
892 default:
893 // This thread owns a window, so this handles all the other random messages we get
894 ::TranslateMessage(&msg);
895 ::DispatchMessageW(&msg);
896 break;
897 }
898 }
899 }
900
901LExit:
902 if (uTimerFailedNetworkRetry)
903 {
904 fRet = ::KillTimer(NULL, uTimerFailedNetworkRetry);
905 }
906 if (uTimerSuccessfulNetworkRetry)
907 {
908 fRet = ::KillTimer(NULL, uTimerSuccessfulNetworkRetry);
909 }
910
911 if (pm->hwnd)
912 {
913 ::CloseWindow(pm->hwnd);
914 }
915
916 // Tell all waiter threads to shutdown
917 for (DWORD i = 0; i < pm->cWaiterThreads; ++i)
918 {
919 pWaiterContext = pm->rgWaiterThreads[i].pWaiterContext;
920 if (NULL != pWaiterContext->rgHandles[0])
921 {
922 if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, MON_MESSAGE_STOP, msg.wParam, msg.lParam))
923 {
924 TraceError(HRESULT_FROM_WIN32(::GetLastError()), "Failed to send message to waiter thread to stop");
925 }
926
927 if (!::SetEvent(pWaiterContext->rgHandles[0]))
928 {
929 TraceError(HRESULT_FROM_WIN32(::GetLastError()), "Failed to set event to notify waiter thread of incoming message");
930 }
931 }
932 }
933
934 if (hMonitor != NULL)
935 {
936 ::WSALookupServiceEnd(hMonitor);
937 }
938
939 // Now confirm they're actually shut down before returning
940 for (DWORD i = 0; i < pm->cWaiterThreads; ++i)
941 {
942 pWaiterContext = pm->rgWaiterThreads[i].pWaiterContext;
943 if (NULL != pWaiterContext->hWaiterThread)
944 {
945 ::WaitForSingleObject(pWaiterContext->hWaiterThread, INFINITE);
946 ::CloseHandle(pWaiterContext->hWaiterThread);
947 }
948
949 // Waiter thread can't release these, because coordinator thread uses it to try communicating with waiter thread
950 ReleaseHandle(pWaiterContext->rgHandles[0]);
951 ReleaseMem(pWaiterContext->rgHandles);
952
953 ReleaseMem(pWaiterContext);
954 }
955
956 if (FAILED(hr))
957 {
958 // If coordinator thread fails, notify general callback of an error
959 Assert(pm->vpfMonGeneral);
960 pm->vpfMonGeneral(hr, pm->pvContext);
961 }
962 MonRemoveMessageDestroy(pRemoveMessage);
963 MonRemoveMessageDestroy(pTempRemoveMessage);
964
965 ::WSACleanup();
966
967 return hr;
968}
969
970static HRESULT InitiateWait(
971 __inout MON_REQUEST *pRequest,
972 __inout HANDLE *pHandle
973 )
974{
975 HRESULT hr = S_OK;
976 HRESULT hrTemp = S_OK;
977 DEV_BROADCAST_HANDLE dev = { };
978 BOOL fRedo = FALSE;
979 BOOL fHandleFound;
980 DWORD er = ERROR_SUCCESS;
981 DWORD dwIndex = 0;
982 HKEY hk = NULL;
983 HANDLE hTemp = INVALID_HANDLE_VALUE;
984
985 if (pRequest->hNotify)
986 {
987 UnregisterDeviceNotification(pRequest->hNotify);
988 pRequest->hNotify = NULL;
989 }
990
991 do
992 {
993 fRedo = FALSE;
994 fHandleFound = FALSE;
995
996 for (DWORD i = 0; i < pRequest->cPathHierarchy && !fHandleFound; ++i)
997 {
998 dwIndex = pRequest->cPathHierarchy - i - 1;
999 switch (pRequest->type)
1000 {
1001 case MON_DIRECTORY:
1002 if (INVALID_HANDLE_VALUE != *pHandle)
1003 {
1004 ::FindCloseChangeNotification(*pHandle);
1005 *pHandle = INVALID_HANDLE_VALUE;
1006 }
1007
1008 *pHandle = ::FindFirstChangeNotificationW(pRequest->rgsczPathHierarchy[dwIndex], GetRecursiveFlag(pRequest, dwIndex), FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_SECURITY);
1009 if (INVALID_HANDLE_VALUE == *pHandle)
1010 {
1011 hr = HRESULT_FROM_WIN32(::GetLastError());
1012 if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr || E_ACCESSDENIED == hr)
1013 {
1014 continue;
1015 }
1016 MonExitOnWin32Error(er, hr, "Failed to wait on path %ls", pRequest->rgsczPathHierarchy[dwIndex]);
1017 }
1018 else
1019 {
1020 fHandleFound = TRUE;
1021 hr = S_OK;
1022 }
1023 break;
1024 case MON_REGKEY:
1025 ReleaseRegKey(pRequest->regkey.hkSubKey);
1026 hr = RegOpen(pRequest->regkey.hkRoot, pRequest->rgsczPathHierarchy[dwIndex], KEY_NOTIFY | GetRegKeyBitness(pRequest), &pRequest->regkey.hkSubKey);
1027 if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr)
1028 {
1029 continue;
1030 }
1031 MonExitOnFailure(hr, "Failed to open regkey %ls", pRequest->rgsczPathHierarchy[dwIndex]);
1032
1033 er = ::RegNotifyChangeKeyValue(pRequest->regkey.hkSubKey, GetRecursiveFlag(pRequest, dwIndex), REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_LAST_SET | REG_NOTIFY_CHANGE_SECURITY, *pHandle, TRUE);
1034 ReleaseRegKey(hk);
1035 hr = HRESULT_FROM_WIN32(er);
1036 if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr || HRESULT_FROM_WIN32(ERROR_KEY_DELETED) == hr)
1037 {
1038 continue;
1039 }
1040 else
1041 {
1042 MonExitOnWin32Error(er, hr, "Failed to wait on subkey %ls", pRequest->rgsczPathHierarchy[dwIndex]);
1043
1044 fHandleFound = TRUE;
1045 }
1046
1047 break;
1048 default:
1049 return E_INVALIDARG;
1050 }
1051 }
1052
1053 pRequest->dwPathHierarchyIndex = dwIndex;
1054
1055 // If we're monitoring a parent instead of the real path because the real path didn't exist, double-check the child hasn't been created since.
1056 // If it has, restart the whole loop
1057 if (dwIndex < pRequest->cPathHierarchy - 1)
1058 {
1059 switch (pRequest->type)
1060 {
1061 case MON_DIRECTORY:
1062 hTemp = ::FindFirstChangeNotificationW(pRequest->rgsczPathHierarchy[dwIndex + 1], GetRecursiveFlag(pRequest, dwIndex + 1), FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_SECURITY);
1063 if (INVALID_HANDLE_VALUE != hTemp)
1064 {
1065 ::FindCloseChangeNotification(hTemp);
1066 fRedo = TRUE;
1067 }
1068 break;
1069 case MON_REGKEY:
1070 hrTemp = RegOpen(pRequest->regkey.hkRoot, pRequest->rgsczPathHierarchy[dwIndex + 1], KEY_NOTIFY | GetRegKeyBitness(pRequest), &hk);
1071 ReleaseRegKey(hk);
1072 fRedo = SUCCEEDED(hrTemp);
1073 break;
1074 default:
1075 Assert(false);
1076 }
1077 }
1078 } while (fRedo);
1079
1080 MonExitOnFailure(hr, "Didn't get a successful wait after looping through all available options %ls", pRequest->rgsczPathHierarchy[pRequest->cPathHierarchy - 1]);
1081
1082 if (MON_DIRECTORY == pRequest->type)
1083 {
1084 dev.dbch_size = sizeof(dev);
1085 dev.dbch_devicetype = DBT_DEVTYP_HANDLE;
1086 dev.dbch_handle = *pHandle;
1087 // Ignore failure on this - some drives by design don't support it (like network paths), and the worst that can happen is a
1088 // removable device will be left in use so user cannot gracefully remove
1089 pRequest->hNotify = RegisterDeviceNotification(pRequest->hwnd, &dev, DEVICE_NOTIFY_WINDOW_HANDLE);
1090 }
1091
1092LExit:
1093 ReleaseRegKey(hk);
1094
1095 return hr;
1096}
1097
1098static DWORD WINAPI WaiterThread(
1099 __in_bcount(sizeof(MON_WAITER_CONTEXT)) LPVOID pvContext
1100 )
1101{
1102 HRESULT hr = S_OK;
1103 HRESULT hrTemp = S_OK;
1104 DWORD dwRet = 0;
1105 BOOL fAgain = FALSE;
1106 BOOL fContinue = TRUE;
1107 BOOL fNotify = FALSE;
1108 BOOL fRet = FALSE;
1109 MSG msg = { };
1110 MON_ADD_MESSAGE *pAddMessage = NULL;
1111 MON_REMOVE_MESSAGE *pRemoveMessage = NULL;
1112 MON_WAITER_CONTEXT *pWaiterContext = reinterpret_cast<MON_WAITER_CONTEXT *>(pvContext);
1113 DWORD dwRequestIndex;
1114 DWORD dwNewRequestIndex;
1115 // If we have one or more requests pending notification, this is the period we intend to wait for multiple objects (shortest amount of time to next potential notify)
1116 DWORD dwWait = 0;
1117 DWORD uCurrentTime = 0;
1118 DWORD uLastTimeInMs = ::GetTickCount();
1119 DWORD uDeltaInMs = 0;
1120 DWORD cRequestsPendingBeforeLoop = 0;
1121 LPWSTR sczDirectory = NULL;
1122 bool rgfProcessedIndex[MON_MAX_MONITORS_PER_THREAD + 1] = { };
1123 MON_INTERNAL_TEMPORARY_WAIT * pInternalWait = NULL;
1124
1125 // Ensure the thread has a message queue
1126 ::PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
1127 pWaiterContext->fWaiterThreadMessageQueueInitialized = TRUE;
1128
1129 do
1130 {
1131 dwRet = ::WaitForMultipleObjects(pWaiterContext->cHandles - pWaiterContext->cRequestsFailing, pWaiterContext->rgHandles, FALSE, pWaiterContext->cRequestsPending > 0 ? dwWait : INFINITE);
1132
1133 uCurrentTime = ::GetTickCount();
1134 uDeltaInMs = uCurrentTime - uLastTimeInMs;
1135 uLastTimeInMs = uCurrentTime;
1136
1137 if (WAIT_OBJECT_0 == dwRet)
1138 {
1139 do
1140 {
1141 fRet = ::PeekMessage(&msg, reinterpret_cast<HWND>(-1), 0, 0, PM_REMOVE);
1142 fAgain = fRet;
1143 if (fRet)
1144 {
1145 switch (msg.message)
1146 {
1147 case MON_MESSAGE_ADD:
1148 pAddMessage = reinterpret_cast<MON_ADD_MESSAGE *>(msg.wParam);
1149
1150 // Don't just blindly put it at the end of the array - it must be before any failing requests
1151 // for WaitForMultipleObjects() to succeed
1152 dwNewRequestIndex = pWaiterContext->cRequests - pWaiterContext->cRequestsFailing;
1153 if (FAILED(pAddMessage->request.hrStatus))
1154 {
1155 ++pWaiterContext->cRequestsFailing;
1156 }
1157
1158 hr = MemInsertIntoArray(reinterpret_cast<void **>(&pWaiterContext->rgHandles), dwNewRequestIndex + 1, 1, pWaiterContext->cHandles, sizeof(HANDLE), MON_ARRAY_GROWTH);
1159 MonExitOnFailure(hr, "Failed to insert additional handle");
1160 ++pWaiterContext->cHandles;
1161
1162 // Ugh - directory types start with INVALID_HANDLE_VALUE instead of NULL
1163 if (MON_DIRECTORY == pAddMessage->request.type)
1164 {
1165 pWaiterContext->rgHandles[dwNewRequestIndex + 1] = INVALID_HANDLE_VALUE;
1166 }
1167
1168 hr = MemInsertIntoArray(reinterpret_cast<void **>(&pWaiterContext->rgRequests), dwNewRequestIndex, 1, pWaiterContext->cRequests, sizeof(MON_REQUEST), MON_ARRAY_GROWTH);
1169 MonExitOnFailure(hr, "Failed to insert additional request struct");
1170 ++pWaiterContext->cRequests;
1171
1172 pWaiterContext->rgRequests[dwNewRequestIndex] = pAddMessage->request;
1173 pWaiterContext->rgHandles[dwNewRequestIndex + 1] = pAddMessage->handle;
1174
1175 ReleaseNullMem(pAddMessage);
1176 break;
1177
1178 case MON_MESSAGE_REMOVE:
1179 pRemoveMessage = reinterpret_cast<MON_REMOVE_MESSAGE *>(msg.wParam);
1180
1181 // Find the request to remove
1182 hr = FindRequestIndex(pWaiterContext, pRemoveMessage, &dwRequestIndex);
1183 if (E_NOTFOUND == hr)
1184 {
1185 // Coordinator sends removes blindly to all waiter threads, so maybe this one wasn't intended for us
1186 hr = S_OK;
1187 }
1188 else
1189 {
1190 MonExitOnFailure(hr, "Failed to find request index for remove message");
1191
1192 hr = RemoveRequest(pWaiterContext, dwRequestIndex);
1193 MonExitOnFailure(hr, "Failed to remove request after request from coordinator thread.");
1194 }
1195
1196 MonRemoveMessageDestroy(pRemoveMessage);
1197 pRemoveMessage = NULL;
1198 break;
1199
1200 case MON_MESSAGE_NETWORK_RETRY_FAILED_NETWORK_WAITS:
1201 if (::PeekMessage(&msg, NULL, MON_MESSAGE_NETWORK_RETRY_FAILED_NETWORK_WAITS, MON_MESSAGE_NETWORK_RETRY_FAILED_NETWORK_WAITS, PM_NOREMOVE))
1202 {
1203 // If there is another a pending retry failed wait message, skip this one
1204 continue;
1205 }
1206
1207 ZeroMemory(rgfProcessedIndex, sizeof(rgfProcessedIndex));
1208 for (DWORD i = 0; i < pWaiterContext->cRequests; ++i)
1209 {
1210 if (rgfProcessedIndex[i])
1211 {
1212 // if we already processed this item due to UpdateWaitStatus swapping array indices, then skip it
1213 continue;
1214 }
1215
1216 if (MON_DIRECTORY == pWaiterContext->rgRequests[i].type && pWaiterContext->rgRequests[i].fNetwork && FAILED(pWaiterContext->rgRequests[i].hrStatus))
1217 {
1218 // This is not a failure, just record this in the request's status
1219 hrTemp = InitiateWait(pWaiterContext->rgRequests + i, pWaiterContext->rgHandles + i + 1);
1220
1221 hr = UpdateWaitStatus(hrTemp, pWaiterContext, i, &dwNewRequestIndex);
1222 MonExitOnFailure(hr, "Failed to update wait status");
1223 hrTemp = S_OK;
1224
1225 if (dwNewRequestIndex != i)
1226 {
1227 // If this request was moved to the end of the list, reprocess this index and mark the new index for skipping
1228 rgfProcessedIndex[dwNewRequestIndex] = true;
1229 --i;
1230 }
1231 }
1232 }
1233 break;
1234
1235 case MON_MESSAGE_NETWORK_RETRY_SUCCESSFUL_NETWORK_WAITS:
1236 if (::PeekMessage(&msg, NULL, MON_MESSAGE_NETWORK_RETRY_SUCCESSFUL_NETWORK_WAITS, MON_MESSAGE_NETWORK_RETRY_SUCCESSFUL_NETWORK_WAITS, PM_NOREMOVE))
1237 {
1238 // If there is another a pending retry successful wait message, skip this one
1239 continue;
1240 }
1241
1242 ZeroMemory(rgfProcessedIndex, sizeof(rgfProcessedIndex));
1243 for (DWORD i = 0; i < pWaiterContext->cRequests; ++i)
1244 {
1245 if (rgfProcessedIndex[i])
1246 {
1247 // if we already processed this item due to UpdateWaitStatus swapping array indices, then skip it
1248 continue;
1249 }
1250
1251 if (MON_DIRECTORY == pWaiterContext->rgRequests[i].type && pWaiterContext->rgRequests[i].fNetwork && SUCCEEDED(pWaiterContext->rgRequests[i].hrStatus))
1252 {
1253 // This is not a failure, just record this in the request's status
1254 hrTemp = InitiateWait(pWaiterContext->rgRequests + i, pWaiterContext->rgHandles + i + 1);
1255
1256 hr = UpdateWaitStatus(hrTemp, pWaiterContext, i, &dwNewRequestIndex);
1257 MonExitOnFailure(hr, "Failed to update wait status");
1258 hrTemp = S_OK;
1259
1260 if (dwNewRequestIndex != i)
1261 {
1262 // If this request was moved to the end of the list, reprocess this index and mark the new index for skipping
1263 rgfProcessedIndex[dwNewRequestIndex] = true;
1264 --i;
1265 }
1266 }
1267 }
1268 break;
1269
1270 case MON_MESSAGE_NETWORK_STATUS_UPDATE:
1271 if (::PeekMessage(&msg, NULL, MON_MESSAGE_NETWORK_STATUS_UPDATE, MON_MESSAGE_NETWORK_STATUS_UPDATE, PM_NOREMOVE))
1272 {
1273 // If there is another a pending network status update message, skip this one
1274 continue;
1275 }
1276
1277 ZeroMemory(rgfProcessedIndex, sizeof(rgfProcessedIndex));
1278 for (DWORD i = 0; i < pWaiterContext->cRequests; ++i)
1279 {
1280 if (rgfProcessedIndex[i])
1281 {
1282 // if we already processed this item due to UpdateWaitStatus swapping array indices, then skip it
1283 continue;
1284 }
1285
1286 if (MON_DIRECTORY == pWaiterContext->rgRequests[i].type && pWaiterContext->rgRequests[i].fNetwork)
1287 {
1288 // Failures here get recorded in the request's status
1289 hrTemp = InitiateWait(pWaiterContext->rgRequests + i, pWaiterContext->rgHandles + i + 1);
1290
1291 hr = UpdateWaitStatus(hrTemp, pWaiterContext, i, &dwNewRequestIndex);
1292 MonExitOnFailure(hr, "Failed to update wait status");
1293 hrTemp = S_OK;
1294
1295 if (dwNewRequestIndex != i)
1296 {
1297 // If this request was moved to the end of the list, reprocess this index and mark the new index for skipping
1298 rgfProcessedIndex[dwNewRequestIndex] = true;
1299 --i;
1300 }
1301 }
1302 }
1303 break;
1304
1305 case MON_MESSAGE_DRIVE_STATUS_UPDATE:
1306 ZeroMemory(rgfProcessedIndex, sizeof(rgfProcessedIndex));
1307 for (DWORD i = 0; i < pWaiterContext->cRequests; ++i)
1308 {
1309 if (rgfProcessedIndex[i])
1310 {
1311 // if we already processed this item due to UpdateWaitStatus swapping array indices, then skip it
1312 continue;
1313 }
1314
1315 if (MON_DIRECTORY == pWaiterContext->rgRequests[i].type && pWaiterContext->rgRequests[i].sczOriginalPathRequest[0] == static_cast<WCHAR>(msg.wParam))
1316 {
1317 // Failures here get recorded in the request's status
1318 if (static_cast<BOOL>(msg.lParam))
1319 {
1320 hrTemp = InitiateWait(pWaiterContext->rgRequests + i, pWaiterContext->rgHandles + i + 1);
1321 }
1322 else
1323 {
1324 // If the message says the drive is disconnected, don't even try to wait, just mark it as gone
1325 hrTemp = E_PATHNOTFOUND;
1326 }
1327
1328 hr = UpdateWaitStatus(hrTemp, pWaiterContext, i, &dwNewRequestIndex);
1329 MonExitOnFailure(hr, "Failed to update wait status");
1330 hrTemp = S_OK;
1331
1332 if (dwNewRequestIndex != i)
1333 {
1334 // If this request was moved to the end of the list, reprocess this index and mark the new index for skipping
1335 rgfProcessedIndex[dwNewRequestIndex] = true;
1336 --i;
1337 }
1338 }
1339 }
1340 break;
1341
1342 case MON_MESSAGE_DRIVE_QUERY_REMOVE:
1343 pInternalWait = reinterpret_cast<MON_INTERNAL_TEMPORARY_WAIT *>(msg.wParam);
1344 // Only do any work if message is not yet out of date
1345 // While it could become out of date while doing this processing, sending thread will check response to guard against this
1346 if (pInternalWait->dwSendIteration == static_cast<DWORD>(msg.lParam))
1347 {
1348 for (DWORD i = 0; i < pWaiterContext->cRequests; ++i)
1349 {
1350 if (MON_DIRECTORY == pWaiterContext->rgRequests[i].type && pWaiterContext->rgHandles[i + 1] == reinterpret_cast<HANDLE>(pInternalWait->pvContext))
1351 {
1352 // Release handles ASAP so the remove request will succeed
1353 if (pWaiterContext->rgRequests[i].hNotify)
1354 {
1355 UnregisterDeviceNotification(pWaiterContext->rgRequests[i].hNotify);
1356 pWaiterContext->rgRequests[i].hNotify = NULL;
1357 }
1358 ::FindCloseChangeNotification(pWaiterContext->rgHandles[i + 1]);
1359 pWaiterContext->rgHandles[i + 1] = INVALID_HANDLE_VALUE;
1360
1361 // Reply to unblock our reply to the remove request
1362 pInternalWait->dwReceiveIteration = static_cast<DWORD>(msg.lParam);
1363 if (!::SetEvent(pInternalWait->hWait))
1364 {
1365 TraceError(HRESULT_FROM_WIN32(::GetLastError()), "Failed to set event to notify coordinator thread that removable device handle was released, this could be due to wndproc no longer waiting for waiter thread's response");
1366 }
1367
1368 // Drive is disconnecting, don't even try to wait, just mark it as gone
1369 hrTemp = E_PATHNOTFOUND;
1370
1371 hr = UpdateWaitStatus(hrTemp, pWaiterContext, i, &dwNewRequestIndex);
1372 MonExitOnFailure(hr, "Failed to update wait status");
1373 hrTemp = S_OK;
1374 break;
1375 }
1376 }
1377 }
1378 break;
1379
1380 case MON_MESSAGE_STOP:
1381 // Stop requested, so abort the whole thread
1382 Trace(REPORT_DEBUG, "Waiter thread was told to stop");
1383 fAgain = FALSE;
1384 fContinue = FALSE;
1385 ExitFunction1(hr = static_cast<HRESULT>(msg.wParam));
1386
1387 default:
1388 Assert(false);
1389 break;
1390 }
1391 }
1392 } while (fAgain);
1393 }
1394 else if (dwRet > WAIT_OBJECT_0 && dwRet - WAIT_OBJECT_0 < pWaiterContext->cHandles)
1395 {
1396 // OK a handle fired - only notify if it's the actual target, and not just some parent waiting for the target child to exist
1397 dwRequestIndex = dwRet - WAIT_OBJECT_0 - 1;
1398 fNotify = (pWaiterContext->rgRequests[dwRequestIndex].dwPathHierarchyIndex == pWaiterContext->rgRequests[dwRequestIndex].cPathHierarchy - 1);
1399
1400 // Initiate re-waits before we notify callback, to ensure we don't miss a single update
1401 hrTemp = InitiateWait(pWaiterContext->rgRequests + dwRequestIndex, pWaiterContext->rgHandles + dwRequestIndex + 1);
1402 hr = UpdateWaitStatus(hrTemp, pWaiterContext, dwRequestIndex, &dwRequestIndex);
1403 MonExitOnFailure(hr, "Failed to update wait status");
1404 hrTemp = S_OK;
1405
1406 // If there were no errors and we were already waiting on the right target, or if we weren't yet but are able to now, it's a successful notify
1407 if (SUCCEEDED(pWaiterContext->rgRequests[dwRequestIndex].hrStatus) && (fNotify || (pWaiterContext->rgRequests[dwRequestIndex].dwPathHierarchyIndex == pWaiterContext->rgRequests[dwRequestIndex].cPathHierarchy - 1)))
1408 {
1409 Trace(REPORT_DEBUG, "Changes detected, waiting for silence period index %u", dwRequestIndex);
1410
1411 if (0 < pWaiterContext->rgRequests[dwRequestIndex].dwMaxSilencePeriodInMs)
1412 {
1413 pWaiterContext->rgRequests[dwRequestIndex].dwSilencePeriodInMs = 0;
1414 pWaiterContext->rgRequests[dwRequestIndex].fSkipDeltaAdd = TRUE;
1415
1416 if (!pWaiterContext->rgRequests[dwRequestIndex].fPendingFire)
1417 {
1418 pWaiterContext->rgRequests[dwRequestIndex].fPendingFire = TRUE;
1419 ++pWaiterContext->cRequestsPending;
1420 }
1421 }
1422 else
1423 {
1424 // If no silence period, notify immediately
1425 Notify(S_OK, pWaiterContext, pWaiterContext->rgRequests + dwRequestIndex);
1426 }
1427 }
1428 }
1429 else if (WAIT_TIMEOUT != dwRet)
1430 {
1431 MonExitWithLastError(hr, "Failed to wait for multiple objects with return code %u", dwRet);
1432 }
1433
1434 // OK, now that we've checked all triggered handles (resetting silence period timers appropriately), check for any pending notifications that we can finally fire
1435 // And set dwWait appropriately so we awaken at the right time to fire the next pending notification (in case no further writes occur during that time)
1436 if (0 < pWaiterContext->cRequestsPending)
1437 {
1438 // Start at max value and find the lowest wait we can below that
1439 dwWait = DWORD_MAX;
1440 cRequestsPendingBeforeLoop = pWaiterContext->cRequestsPending;
1441
1442 for (DWORD i = 0; i < pWaiterContext->cRequests; ++i)
1443 {
1444 if (pWaiterContext->rgRequests[i].fPendingFire)
1445 {
1446 if (0 == cRequestsPendingBeforeLoop)
1447 {
1448 Assert(FALSE);
1449 hr = HRESULT_FROM_WIN32(ERROR_EA_LIST_INCONSISTENT);
1450 MonExitOnFailure(hr, "Phantom pending fires were found!");
1451 }
1452 --cRequestsPendingBeforeLoop;
1453
1454 dwRequestIndex = i;
1455
1456 if (pWaiterContext->rgRequests[dwRequestIndex].fSkipDeltaAdd)
1457 {
1458 pWaiterContext->rgRequests[dwRequestIndex].fSkipDeltaAdd = FALSE;
1459 }
1460 else
1461 {
1462 pWaiterContext->rgRequests[dwRequestIndex].dwSilencePeriodInMs += uDeltaInMs;
1463 }
1464
1465 // silence period has elapsed without further notifications, so reset pending-related variables, and finally fire a notify!
1466 if (pWaiterContext->rgRequests[dwRequestIndex].dwSilencePeriodInMs >= pWaiterContext->rgRequests[dwRequestIndex].dwMaxSilencePeriodInMs)
1467 {
1468 Trace(REPORT_DEBUG, "Silence period surpassed, notifying %u ms late", pWaiterContext->rgRequests[dwRequestIndex].dwSilencePeriodInMs - pWaiterContext->rgRequests[dwRequestIndex].dwMaxSilencePeriodInMs);
1469 Notify(S_OK, pWaiterContext, pWaiterContext->rgRequests + dwRequestIndex);
1470 }
1471 else
1472 {
1473 // set dwWait to the shortest interval period so that if no changes occur, WaitForMultipleObjects
1474 // wakes the thread back up when it's time to fire the next pending notification
1475 if (dwWait > pWaiterContext->rgRequests[dwRequestIndex].dwMaxSilencePeriodInMs - pWaiterContext->rgRequests[dwRequestIndex].dwSilencePeriodInMs)
1476 {
1477 dwWait = pWaiterContext->rgRequests[dwRequestIndex].dwMaxSilencePeriodInMs - pWaiterContext->rgRequests[dwRequestIndex].dwSilencePeriodInMs;
1478 }
1479 }
1480 }
1481 }
1482
1483 // Some post-loop list validation for sanity checking
1484 if (0 < cRequestsPendingBeforeLoop)
1485 {
1486 Assert(FALSE);
1487 hr = HRESULT_FROM_WIN32(PEERDIST_ERROR_MISSING_DATA);
1488 MonExitOnFailure(hr, "Missing %u pending fires! Total pending fires: %u, wait: %u", cRequestsPendingBeforeLoop, pWaiterContext->cRequestsPending, dwWait);
1489 }
1490 if (0 < pWaiterContext->cRequestsPending && DWORD_MAX == dwWait)
1491 {
1492 Assert(FALSE);
1493 hr = HRESULT_FROM_WIN32(ERROR_CANT_WAIT);
1494 MonExitOnFailure(hr, "Pending fires exist (%u), but wait was infinite", cRequestsPendingBeforeLoop);
1495 }
1496 }
1497 } while (fContinue);
1498
1499 // Don't bother firing pending notifications. We were told to stop monitoring, so client doesn't care.
1500
1501LExit:
1502 ReleaseStr(sczDirectory);
1503 MonAddMessageDestroy(pAddMessage);
1504 MonRemoveMessageDestroy(pRemoveMessage);
1505
1506 for (DWORD i = 0; i < pWaiterContext->cRequests; ++i)
1507 {
1508 MonRequestDestroy(pWaiterContext->rgRequests + i);
1509
1510 switch (pWaiterContext->rgRequests[i].type)
1511 {
1512 case MON_DIRECTORY:
1513 if (INVALID_HANDLE_VALUE != pWaiterContext->rgHandles[i + 1])
1514 {
1515 ::FindCloseChangeNotification(pWaiterContext->rgHandles[i + 1]);
1516 }
1517 break;
1518 case MON_REGKEY:
1519 ReleaseHandle(pWaiterContext->rgHandles[i + 1]);
1520 break;
1521 default:
1522 Assert(false);
1523 }
1524 }
1525
1526 if (FAILED(hr))
1527 {
1528 // If waiter thread fails, notify general callback of an error
1529 Assert(pWaiterContext->vpfMonGeneral);
1530 pWaiterContext->vpfMonGeneral(hr, pWaiterContext->pvContext);
1531
1532 // And tell coordinator to shut all other waiters down
1533 if (!::PostThreadMessageW(pWaiterContext->dwCoordinatorThreadId, MON_MESSAGE_STOP, 0, 0))
1534 {
1535 TraceError(HRESULT_FROM_WIN32(::GetLastError()), "Failed to send message to coordinator thread to stop (due to general failure).");
1536 }
1537 }
1538
1539 return hr;
1540}
1541
1542static void Notify(
1543 __in HRESULT hr,
1544 __in MON_WAITER_CONTEXT *pWaiterContext,
1545 __in MON_REQUEST *pRequest
1546 )
1547{
1548 if (pRequest->fPendingFire)
1549 {
1550 --pWaiterContext->cRequestsPending;
1551 }
1552
1553 pRequest->fPendingFire = FALSE;
1554 pRequest->fSkipDeltaAdd = FALSE;
1555 pRequest->dwSilencePeriodInMs = 0;
1556
1557 switch (pRequest->type)
1558 {
1559 case MON_DIRECTORY:
1560 Assert(pWaiterContext->vpfMonDirectory);
1561 pWaiterContext->vpfMonDirectory(hr, pRequest->sczOriginalPathRequest, pRequest->fRecursive, pWaiterContext->pvContext, pRequest->pvContext);
1562 break;
1563 case MON_REGKEY:
1564 Assert(pWaiterContext->vpfMonRegKey);
1565 pWaiterContext->vpfMonRegKey(hr, pRequest->regkey.hkRoot, pRequest->rgsczPathHierarchy[pRequest->cPathHierarchy - 1], pRequest->regkey.kbKeyBitness, pRequest->fRecursive, pWaiterContext->pvContext, pRequest->pvContext);
1566 break;
1567 default:
1568 Assert(false);
1569 }
1570}
1571
1572static BOOL GetRecursiveFlag(
1573 __in MON_REQUEST *pRequest,
1574 __in DWORD dwIndex
1575 )
1576{
1577 if (pRequest->cPathHierarchy - 1 == dwIndex)
1578 {
1579 return pRequest->fRecursive;
1580 }
1581 else
1582 {
1583 return FALSE;
1584 }
1585}
1586
1587static HRESULT FindRequestIndex(
1588 __in MON_WAITER_CONTEXT *pWaiterContext,
1589 __in MON_REMOVE_MESSAGE *pMessage,
1590 __out DWORD *pdwIndex
1591 )
1592{
1593 HRESULT hr = S_OK;
1594
1595 for (DWORD i = 0; i < pWaiterContext->cRequests; ++i)
1596 {
1597 if (pWaiterContext->rgRequests[i].type == pMessage->type)
1598 {
1599 switch (pWaiterContext->rgRequests[i].type)
1600 {
1601 case MON_DIRECTORY:
1602 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pWaiterContext->rgRequests[i].rgsczPathHierarchy[pWaiterContext->rgRequests[i].cPathHierarchy - 1], -1, pMessage->directory.sczDirectory, -1) && pWaiterContext->rgRequests[i].fRecursive == pMessage->fRecursive)
1603 {
1604 *pdwIndex = i;
1605 ExitFunction1(hr = S_OK);
1606 }
1607 break;
1608 case MON_REGKEY:
1609 if (reinterpret_cast<DWORD_PTR>(pMessage->regkey.hkRoot) == reinterpret_cast<DWORD_PTR>(pWaiterContext->rgRequests[i].regkey.hkRoot) && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pWaiterContext->rgRequests[i].rgsczPathHierarchy[pWaiterContext->rgRequests[i].cPathHierarchy - 1], -1, pMessage->regkey.sczSubKey, -1) && pWaiterContext->rgRequests[i].fRecursive == pMessage->fRecursive && pWaiterContext->rgRequests[i].regkey.kbKeyBitness == pMessage->regkey.kbKeyBitness)
1610 {
1611 *pdwIndex = i;
1612 ExitFunction1(hr = S_OK);
1613 }
1614 break;
1615 default:
1616 Assert(false);
1617 }
1618 }
1619 }
1620
1621 hr = E_NOTFOUND;
1622
1623LExit:
1624 return hr;
1625}
1626
1627static HRESULT RemoveRequest(
1628 __inout MON_WAITER_CONTEXT *pWaiterContext,
1629 __in DWORD dwRequestIndex
1630 )
1631{
1632 HRESULT hr = S_OK;
1633
1634 MonRequestDestroy(pWaiterContext->rgRequests + dwRequestIndex);
1635
1636 switch (pWaiterContext->rgRequests[dwRequestIndex].type)
1637 {
1638 case MON_DIRECTORY:
1639 if (pWaiterContext->rgHandles[dwRequestIndex + 1] != INVALID_HANDLE_VALUE)
1640 {
1641 ::FindCloseChangeNotification(pWaiterContext->rgHandles[dwRequestIndex + 1]);
1642 }
1643 break;
1644 case MON_REGKEY:
1645 ReleaseHandle(pWaiterContext->rgHandles[dwRequestIndex + 1]);
1646 break;
1647 default:
1648 Assert(false);
1649 }
1650
1651 if (pWaiterContext->rgRequests[dwRequestIndex].fPendingFire)
1652 {
1653 --pWaiterContext->cRequestsPending;
1654 }
1655
1656 if (FAILED(pWaiterContext->rgRequests[dwRequestIndex].hrStatus))
1657 {
1658 --pWaiterContext->cRequestsFailing;
1659 }
1660
1661 MemRemoveFromArray(reinterpret_cast<void *>(pWaiterContext->rgHandles), dwRequestIndex + 1, 1, pWaiterContext->cHandles, sizeof(HANDLE), TRUE);
1662 --pWaiterContext->cHandles;
1663 MemRemoveFromArray(reinterpret_cast<void *>(pWaiterContext->rgRequests), dwRequestIndex, 1, pWaiterContext->cRequests, sizeof(MON_REQUEST), TRUE);
1664 --pWaiterContext->cRequests;
1665
1666 // Notify coordinator thread that a wait was removed
1667 if (!::PostThreadMessageW(pWaiterContext->dwCoordinatorThreadId, MON_MESSAGE_REMOVED, static_cast<WPARAM>(::GetCurrentThreadId()), 0))
1668 {
1669 MonExitWithLastError(hr, "Failed to send message to coordinator thread to confirm directory was removed.");
1670 }
1671
1672LExit:
1673 return hr;
1674}
1675
1676static REGSAM GetRegKeyBitness(
1677 __in MON_REQUEST *pRequest
1678 )
1679{
1680 if (REG_KEY_32BIT == pRequest->regkey.kbKeyBitness)
1681 {
1682 return KEY_WOW64_32KEY;
1683 }
1684 else if (REG_KEY_64BIT == pRequest->regkey.kbKeyBitness)
1685 {
1686 return KEY_WOW64_64KEY;
1687 }
1688 else
1689 {
1690 return 0;
1691 }
1692}
1693
1694static HRESULT DuplicateRemoveMessage(
1695 __in MON_REMOVE_MESSAGE *pMessage,
1696 __out MON_REMOVE_MESSAGE **ppMessage
1697 )
1698{
1699 HRESULT hr = S_OK;
1700
1701 *ppMessage = reinterpret_cast<MON_REMOVE_MESSAGE *>(MemAlloc(sizeof(MON_REMOVE_MESSAGE), TRUE));
1702 MonExitOnNull(*ppMessage, hr, E_OUTOFMEMORY, "Failed to allocate copy of remove message");
1703
1704 (*ppMessage)->type = pMessage->type;
1705 (*ppMessage)->fRecursive = pMessage->fRecursive;
1706
1707 switch (pMessage->type)
1708 {
1709 case MON_DIRECTORY:
1710 hr = StrAllocString(&(*ppMessage)->directory.sczDirectory, pMessage->directory.sczDirectory, 0);
1711 MonExitOnFailure(hr, "Failed to copy directory");
1712 break;
1713 case MON_REGKEY:
1714 (*ppMessage)->regkey.hkRoot = pMessage->regkey.hkRoot;
1715 (*ppMessage)->regkey.kbKeyBitness = pMessage->regkey.kbKeyBitness;
1716 hr = StrAllocString(&(*ppMessage)->regkey.sczSubKey, pMessage->regkey.sczSubKey, 0);
1717 MonExitOnFailure(hr, "Failed to copy subkey");
1718 break;
1719 default:
1720 Assert(false);
1721 break;
1722 }
1723
1724LExit:
1725 return hr;
1726}
1727
1728static LRESULT CALLBACK MonWndProc(
1729 __in HWND hWnd,
1730 __in UINT uMsg,
1731 __in WPARAM wParam,
1732 __in LPARAM lParam
1733 )
1734{
1735 HRESULT hr = S_OK;
1736 DEV_BROADCAST_HDR *pHdr = NULL;
1737 DEV_BROADCAST_HANDLE *pHandle = NULL;
1738 DEV_BROADCAST_VOLUME *pVolume = NULL;
1739 DWORD dwUnitMask = 0;
1740 DWORD er = ERROR_SUCCESS;
1741 WCHAR chDrive = L'\0';
1742 BOOL fArrival = FALSE;
1743 BOOL fReturnTrue = FALSE;
1744 CREATESTRUCT *pCreateStruct = NULL;
1745 MON_WAITER_CONTEXT *pWaiterContext = NULL;
1746 MON_STRUCT *pm = NULL;
1747
1748 // keep track of the MON_STRUCT pointer that was passed in on init, associate it with the window
1749 if (WM_CREATE == uMsg)
1750 {
1751 pCreateStruct = reinterpret_cast<CREATESTRUCT *>(lParam);
1752 if (pCreateStruct)
1753 {
1754 ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pCreateStruct->lpCreateParams));
1755 }
1756 }
1757 else if (WM_NCDESTROY == uMsg)
1758 {
1759 ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0);
1760 }
1761
1762 // Note this message ONLY comes in through WndProc, it isn't visible from the GetMessage loop.
1763 else if (WM_DEVICECHANGE == uMsg)
1764 {
1765 if (DBT_DEVICEARRIVAL == wParam || DBT_DEVICEREMOVECOMPLETE == wParam)
1766 {
1767 fArrival = DBT_DEVICEARRIVAL == wParam;
1768
1769 pHdr = reinterpret_cast<DEV_BROADCAST_HDR*>(lParam);
1770 if (DBT_DEVTYP_VOLUME == pHdr->dbch_devicetype)
1771 {
1772 pVolume = reinterpret_cast<DEV_BROADCAST_VOLUME*>(lParam);
1773 dwUnitMask = pVolume->dbcv_unitmask;
1774 chDrive = L'a';
1775 while (0 < dwUnitMask)
1776 {
1777 if (dwUnitMask & 0x1)
1778 {
1779 // This drive had a status update, so send it out to all threads
1780 if (!::PostThreadMessageW(::GetCurrentThreadId(), MON_MESSAGE_DRIVE_STATUS_UPDATE, static_cast<WPARAM>(chDrive), static_cast<LPARAM>(fArrival)))
1781 {
1782 MonExitWithLastError(hr, "Failed to send drive status update with drive %wc and arrival %ls", chDrive, fArrival ? L"TRUE" : L"FALSE");
1783 }
1784 }
1785 dwUnitMask >>= 1;
1786 ++chDrive;
1787
1788 if (chDrive == 'z')
1789 {
1790 hr = E_UNEXPECTED;
1791 MonExitOnFailure(hr, "UnitMask showed drives beyond z:. Remaining UnitMask at this point: %u", dwUnitMask);
1792 }
1793 }
1794 }
1795 }
1796 // We can only process device query remove messages if we have a MON_STRUCT pointer
1797 else if (DBT_DEVICEQUERYREMOVE == wParam)
1798 {
1799 pm = reinterpret_cast<MON_STRUCT*>(::GetWindowLongPtrW(hWnd, GWLP_USERDATA));
1800 if (!pm)
1801 {
1802 hr = E_POINTER;
1803 MonExitOnFailure(hr, "DBT_DEVICEQUERYREMOVE message received with no MON_STRUCT pointer, so message was ignored");
1804 }
1805
1806 fReturnTrue = TRUE;
1807
1808 pHdr = reinterpret_cast<DEV_BROADCAST_HDR*>(lParam);
1809 if (DBT_DEVTYP_HANDLE == pHdr->dbch_devicetype)
1810 {
1811 // We must wait for the actual wait handle to be released by waiter thread before telling windows to proceed with device removal, otherwise it could fail
1812 // due to handles still being open, so use a MON_INTERNAL_TEMPORARY_WAIT struct to send and receive a reply from a waiter thread
1813 pm->internalWait.hWait = ::CreateEventW(NULL, TRUE, FALSE, NULL);
1814 MonExitOnNullWithLastError(pm->internalWait.hWait, hr, "Failed to create anonymous event for waiter to notify wndproc device can be removed");
1815
1816 pHandle = reinterpret_cast<DEV_BROADCAST_HANDLE*>(lParam);
1817 pm->internalWait.pvContext = pHandle->dbch_handle;
1818 pm->internalWait.dwReceiveIteration = pm->internalWait.dwSendIteration - 1;
1819 // This drive had a status update, so send it out to all threads
1820 for (DWORD i = 0; i < pm->cWaiterThreads; ++i)
1821 {
1822 pWaiterContext = pm->rgWaiterThreads[i].pWaiterContext;
1823
1824 if (!::PostThreadMessageW(pWaiterContext->dwWaiterThreadId, MON_MESSAGE_DRIVE_QUERY_REMOVE, reinterpret_cast<WPARAM>(&pm->internalWait), static_cast<LPARAM>(pm->internalWait.dwSendIteration)))
1825 {
1826 MonExitWithLastError(hr, "Failed to send message to waiter thread to notify of drive query remove");
1827 }
1828
1829 if (!::SetEvent(pWaiterContext->rgHandles[0]))
1830 {
1831 MonExitWithLastError(hr, "Failed to set event to notify waiter thread of incoming drive query remove message");
1832 }
1833 }
1834
1835 er = ::WaitForSingleObject(pm->internalWait.hWait, MON_THREAD_WAIT_REMOVE_DEVICE);
1836 // Make sure any waiter thread processing really old messages can immediately know that we're no longer waiting for a response
1837 if (WAIT_OBJECT_0 == er)
1838 {
1839 // If the response ID matches what we sent, we actually got a valid reply!
1840 if (pm->internalWait.dwReceiveIteration != pm->internalWait.dwSendIteration)
1841 {
1842 TraceError(HRESULT_FROM_WIN32(er), "Waiter thread received wrong ID reply");
1843 }
1844 }
1845 else if (WAIT_TIMEOUT == er)
1846 {
1847 TraceError(HRESULT_FROM_WIN32(er), "No response from any waiter thread for query remove message");
1848 }
1849 else
1850 {
1851 MonExitWithLastError(hr, "WaitForSingleObject failed with non-timeout reason while waiting for response from waiter thread");
1852 }
1853 ++pm->internalWait.dwSendIteration;
1854 }
1855 }
1856 }
1857
1858LExit:
1859 if (pm)
1860 {
1861 ReleaseHandle(pm->internalWait.hWait);
1862 }
1863
1864 if (fReturnTrue)
1865 {
1866 return TRUE;
1867 }
1868 else
1869 {
1870 return ::DefWindowProcW(hWnd, uMsg, wParam, lParam);
1871 }
1872}
1873
1874static HRESULT CreateMonWindow(
1875 __in MON_STRUCT *pm,
1876 __out HWND *pHwnd
1877 )
1878{
1879 HRESULT hr = S_OK;
1880 WNDCLASSW wc = { };
1881
1882 wc.lpfnWndProc = MonWndProc;
1883 wc.hInstance = ::GetModuleHandleW(NULL);
1884 wc.lpszClassName = MONUTIL_WINDOW_CLASS;
1885 if (!::RegisterClassW(&wc))
1886 {
1887 if (ERROR_CLASS_ALREADY_EXISTS != ::GetLastError())
1888 {
1889 MonExitWithLastError(hr, "Failed to register MonUtil window class.");
1890 }
1891 }
1892
1893 *pHwnd = ::CreateWindowExW(0, wc.lpszClassName, L"", 0, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, HWND_DESKTOP, NULL, wc.hInstance, pm);
1894 MonExitOnNullWithLastError(*pHwnd, hr, "Failed to create window.");
1895
1896 // Rumor has it that drive arrival / removal events can be lost in the rare event that some other application higher up in z-order is hanging if we don't make our window topmost
1897 // SWP_NOACTIVATE is important so the currently active window doesn't lose focus
1898 SetWindowPos(*pHwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_DEFERERASE | SWP_NOACTIVATE);
1899
1900LExit:
1901 return hr;
1902}
1903
1904static HRESULT WaitForNetworkChanges(
1905 __inout HANDLE *phMonitor,
1906 __in MON_STRUCT *pm
1907 )
1908{
1909 HRESULT hr = S_OK;
1910 int nResult = 0;
1911 DWORD dwBytesReturned = 0;
1912 WSACOMPLETION wsaCompletion = { };
1913 WSAQUERYSET qsRestrictions = { };
1914
1915 qsRestrictions.dwSize = sizeof(WSAQUERYSET);
1916 qsRestrictions.dwNameSpace = NS_NLA;
1917
1918 if (NULL != *phMonitor)
1919 {
1920 ::WSALookupServiceEnd(*phMonitor);
1921 *phMonitor = NULL;
1922 }
1923
1924 if (::WSALookupServiceBegin(&qsRestrictions, LUP_RETURN_ALL, phMonitor))
1925 {
1926 hr = HRESULT_FROM_WIN32(::WSAGetLastError());
1927 MonExitOnFailure(hr, "WSALookupServiceBegin() failed");
1928 }
1929
1930 wsaCompletion.Type = NSP_NOTIFY_HWND;
1931 wsaCompletion.Parameters.WindowMessage.hWnd = pm->hwnd;
1932 wsaCompletion.Parameters.WindowMessage.uMsg = MON_MESSAGE_NETWORK_STATUS_UPDATE;
1933 nResult = ::WSANSPIoctl(*phMonitor, SIO_NSP_NOTIFY_CHANGE, NULL, 0, NULL, 0, &dwBytesReturned, &wsaCompletion);
1934 if (SOCKET_ERROR != nResult || WSA_IO_PENDING != ::WSAGetLastError())
1935 {
1936 hr = HRESULT_FROM_WIN32(::WSAGetLastError());
1937 if (SUCCEEDED(hr))
1938 {
1939 hr = E_FAIL;
1940 }
1941 MonExitOnFailure(hr, "WSANSPIoctl() failed with return code %i, wsa last error %u", nResult, ::WSAGetLastError());
1942 }
1943
1944LExit:
1945 return hr;
1946}
1947
1948static HRESULT UpdateWaitStatus(
1949 __in HRESULT hrNewStatus,
1950 __inout MON_WAITER_CONTEXT *pWaiterContext,
1951 __in DWORD dwRequestIndex,
1952 __out_opt DWORD *pdwNewRequestIndex
1953 )
1954{
1955 HRESULT hr = S_OK;
1956 DWORD dwNewRequestIndex;
1957 MON_REQUEST *pRequest = pWaiterContext->rgRequests + dwRequestIndex;
1958
1959 if (NULL != pdwNewRequestIndex)
1960 {
1961 *pdwNewRequestIndex = dwRequestIndex;
1962 }
1963
1964 if (SUCCEEDED(pRequest->hrStatus) || SUCCEEDED(hrNewStatus))
1965 {
1966 // If it's a network wait, notify as long as it's new status is successful because we *may* have lost some changes
1967 // before the wait was re-initiated. Otherwise, only notify if there was an interesting status change
1968 if (SUCCEEDED(pRequest->hrStatus) != SUCCEEDED(hrNewStatus) || (pRequest->fNetwork && SUCCEEDED(hrNewStatus)))
1969 {
1970 Notify(hrNewStatus, pWaiterContext, pRequest);
1971 }
1972
1973 if (SUCCEEDED(pRequest->hrStatus) && FAILED(hrNewStatus))
1974 {
1975 // If it's a network wait, notify coordinator thread that a network wait is failing
1976 if (pRequest->fNetwork && !::PostThreadMessageW(pWaiterContext->dwCoordinatorThreadId, MON_MESSAGE_NETWORK_WAIT_FAILED, 0, 0))
1977 {
1978 MonExitWithLastError(hr, "Failed to send message to coordinator thread to notify a network wait started to fail");
1979 }
1980
1981 // Move the failing wait to the end of the list of waits and increment cRequestsFailing so WaitForMultipleObjects isn't passed an invalid handle
1982 ++pWaiterContext->cRequestsFailing;
1983 dwNewRequestIndex = pWaiterContext->cRequests - 1;
1984 MemArraySwapItems(reinterpret_cast<void *>(pWaiterContext->rgHandles), dwRequestIndex + 1, dwNewRequestIndex + 1, sizeof(*pWaiterContext->rgHandles));
1985 MemArraySwapItems(reinterpret_cast<void *>(pWaiterContext->rgRequests), dwRequestIndex, dwNewRequestIndex, sizeof(*pWaiterContext->rgRequests));
1986 // Reset pRequest to the newly swapped item
1987 pRequest = pWaiterContext->rgRequests + dwNewRequestIndex;
1988 if (NULL != pdwNewRequestIndex)
1989 {
1990 *pdwNewRequestIndex = dwNewRequestIndex;
1991 }
1992 }
1993 else if (FAILED(pRequest->hrStatus) && SUCCEEDED(hrNewStatus))
1994 {
1995 Assert(pWaiterContext->cRequestsFailing > 0);
1996 // If it's a network wait, notify coordinator thread that a network wait is succeeding again
1997 if (pRequest->fNetwork && !::PostThreadMessageW(pWaiterContext->dwCoordinatorThreadId, MON_MESSAGE_NETWORK_WAIT_SUCCEEDED, 0, 0))
1998 {
1999 MonExitWithLastError(hr, "Failed to send message to coordinator thread to notify a network wait is succeeding again");
2000 }
2001
2002 --pWaiterContext->cRequestsFailing;
2003 dwNewRequestIndex = 0;
2004 MemArraySwapItems(reinterpret_cast<void *>(pWaiterContext->rgHandles), dwRequestIndex + 1, dwNewRequestIndex + 1, sizeof(*pWaiterContext->rgHandles));
2005 MemArraySwapItems(reinterpret_cast<void *>(pWaiterContext->rgRequests), dwRequestIndex, dwNewRequestIndex, sizeof(*pWaiterContext->rgRequests));
2006 // Reset pRequest to the newly swapped item
2007 pRequest = pWaiterContext->rgRequests + dwNewRequestIndex;
2008 if (NULL != pdwNewRequestIndex)
2009 {
2010 *pdwNewRequestIndex = dwNewRequestIndex;
2011 }
2012 }
2013 }
2014
2015 pRequest->hrStatus = hrNewStatus;
2016
2017LExit:
2018 return hr;
2019}
diff --git a/src/libs/dutil/WixToolset.DUtil/osutil.cpp b/src/libs/dutil/WixToolset.DUtil/osutil.cpp
new file mode 100644
index 00000000..880ec3ea
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/osutil.cpp
@@ -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// Exit macros
7#define OsExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_OSUTIL, x, s, __VA_ARGS__)
8#define OsExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_OSUTIL, x, s, __VA_ARGS__)
9#define OsExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_OSUTIL, x, s, __VA_ARGS__)
10#define OsExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_OSUTIL, x, s, __VA_ARGS__)
11#define OsExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_OSUTIL, x, s, __VA_ARGS__)
12#define OsExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_OSUTIL, x, s, __VA_ARGS__)
13#define OsExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_OSUTIL, p, x, e, s, __VA_ARGS__)
14#define OsExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_OSUTIL, p, x, s, __VA_ARGS__)
15#define OsExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_OSUTIL, p, x, e, s, __VA_ARGS__)
16#define OsExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_OSUTIL, p, x, s, __VA_ARGS__)
17#define OsExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_OSUTIL, e, x, s, __VA_ARGS__)
18#define OsExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_OSUTIL, g, x, s, __VA_ARGS__)
19
20typedef NTSTATUS(NTAPI* PFN_RTL_GET_VERSION)(_Out_ PRTL_OSVERSIONINFOEXW lpVersionInformation);
21
22OS_VERSION vOsVersion = OS_VERSION_UNKNOWN;
23DWORD vdwOsServicePack = 0;
24RTL_OSVERSIONINFOEXW vovix = { };
25
26/********************************************************************
27 OsGetVersion
28
29********************************************************************/
30extern "C" void DAPI OsGetVersion(
31 __out OS_VERSION* pVersion,
32 __out DWORD* pdwServicePack
33 )
34{
35 OSVERSIONINFOEXW ovi = { };
36
37 if (OS_VERSION_UNKNOWN == vOsVersion)
38 {
39 ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
40
41#pragma warning (push)
42#pragma warning(suppress: 4996) // deprecated
43#pragma warning (push)
44#pragma warning(suppress: 28159)// deprecated, use other function instead
45 ::GetVersionExW(reinterpret_cast<OSVERSIONINFOW*>(&ovi)); // only fails if version info size is set incorrectly.
46#pragma warning (pop)
47#pragma warning (pop)
48
49 vdwOsServicePack = static_cast<DWORD>(ovi.wServicePackMajor) << 16 | ovi.wServicePackMinor;
50 if (4 == ovi.dwMajorVersion)
51 {
52 vOsVersion = OS_VERSION_WINNT;
53 }
54 else if (5 == ovi.dwMajorVersion)
55 {
56 if (0 == ovi.dwMinorVersion)
57 {
58 vOsVersion = OS_VERSION_WIN2000;
59 }
60 else if (1 == ovi.dwMinorVersion)
61 {
62 vOsVersion = OS_VERSION_WINXP;
63 }
64 else if (2 == ovi.dwMinorVersion)
65 {
66 vOsVersion = OS_VERSION_WIN2003;
67 }
68 else
69 {
70 vOsVersion = OS_VERSION_FUTURE;
71 }
72 }
73 else if (6 == ovi.dwMajorVersion)
74 {
75 if (0 == ovi.dwMinorVersion)
76 {
77 vOsVersion = (VER_NT_WORKSTATION == ovi.wProductType) ? OS_VERSION_VISTA : OS_VERSION_WIN2008;
78 }
79 else if (1 == ovi.dwMinorVersion)
80 {
81 vOsVersion = (VER_NT_WORKSTATION == ovi.wProductType) ? OS_VERSION_WIN7 : OS_VERSION_WIN2008_R2;
82 }
83 else
84 {
85 vOsVersion = OS_VERSION_FUTURE;
86 }
87 }
88 else
89 {
90 vOsVersion = OS_VERSION_FUTURE;
91 }
92 }
93
94 *pVersion = vOsVersion;
95 *pdwServicePack = vdwOsServicePack;
96}
97
98extern "C" HRESULT DAPI OsCouldRunPrivileged(
99 __out BOOL* pfPrivileged
100 )
101{
102 HRESULT hr = S_OK;
103 BOOL fUacEnabled = FALSE;
104 SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
105 PSID AdministratorsGroup = NULL;
106
107 // Do a best effort check to see if UAC is enabled on this machine.
108 OsIsUacEnabled(&fUacEnabled);
109
110 // If UAC is enabled then the process could run privileged by asking to elevate.
111 if (fUacEnabled)
112 {
113 *pfPrivileged = TRUE;
114 }
115 else // no UAC so only privilged if user is in administrators group.
116 {
117 *pfPrivileged = ::AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &AdministratorsGroup);
118 if (*pfPrivileged)
119 {
120 if (!::CheckTokenMembership(NULL, AdministratorsGroup, pfPrivileged))
121 {
122 *pfPrivileged = FALSE;
123 }
124 }
125 }
126
127 ReleaseSid(AdministratorsGroup);
128 return hr;
129}
130
131extern "C" HRESULT DAPI OsIsRunningPrivileged(
132 __out BOOL* pfPrivileged
133 )
134{
135 HRESULT hr = S_OK;
136 UINT er = ERROR_SUCCESS;
137 HANDLE hToken = NULL;
138 TOKEN_ELEVATION_TYPE elevationType = TokenElevationTypeDefault;
139 DWORD dwSize = 0;
140 SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
141 PSID AdministratorsGroup = NULL;
142
143 if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &hToken))
144 {
145 OsExitOnLastError(hr, "Failed to open process token.");
146 }
147
148 if (::GetTokenInformation(hToken, TokenElevationType, &elevationType, sizeof(TOKEN_ELEVATION_TYPE), &dwSize))
149 {
150 *pfPrivileged = (TokenElevationTypeFull == elevationType);
151 ExitFunction1(hr = S_OK);
152 }
153
154 // If it's invalid argument, this means they don't support TokenElevationType, and we should fallback to another check
155 er = ::GetLastError();
156 if (ERROR_INVALID_FUNCTION == er)
157 {
158 er = ERROR_SUCCESS;
159 }
160 OsExitOnWin32Error(er, hr, "Failed to get process token information.");
161
162 // Fallback to this check for some OS's (like XP)
163 *pfPrivileged = ::AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &AdministratorsGroup);
164 if (*pfPrivileged)
165 {
166 if (!::CheckTokenMembership(NULL, AdministratorsGroup, pfPrivileged))
167 {
168 *pfPrivileged = FALSE;
169 }
170 }
171
172LExit:
173 ReleaseSid(AdministratorsGroup);
174
175 if (hToken)
176 {
177 ::CloseHandle(hToken);
178 }
179
180 return hr;
181}
182
183extern "C" HRESULT DAPI OsIsUacEnabled(
184 __out BOOL* pfUacEnabled
185 )
186{
187 HRESULT hr = S_OK;
188 HKEY hk = NULL;
189 DWORD dwUacEnabled = 0;
190
191 *pfUacEnabled = FALSE; // assume UAC not enabled.
192
193 hr = RegOpen(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System", KEY_READ, &hk);
194 if (E_FILENOTFOUND == hr)
195 {
196 ExitFunction1(hr = S_OK);
197 }
198 OsExitOnFailure(hr, "Failed to open system policy key to detect UAC.");
199
200 hr = RegReadNumber(hk, L"EnableLUA", &dwUacEnabled);
201 if (E_FILENOTFOUND == hr)
202 {
203 ExitFunction1(hr = S_OK);
204 }
205 OsExitOnFailure(hr, "Failed to read registry value to detect UAC.");
206
207 *pfUacEnabled = (0 != dwUacEnabled);
208
209LExit:
210 ReleaseRegKey(hk);
211
212 return hr;
213}
214
215HRESULT DAPI OsRtlGetVersion(
216 __inout RTL_OSVERSIONINFOEXW* pOvix
217 )
218{
219 HRESULT hr = S_OK;
220 HMODULE hNtdll = NULL;
221 PFN_RTL_GET_VERSION pfnRtlGetVersion = NULL;
222
223 if (vovix.dwOSVersionInfoSize)
224 {
225 ExitFunction();
226 }
227
228 vovix.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);
229
230 hr = LoadSystemLibrary(L"ntdll.dll", &hNtdll);
231 if (E_MODNOTFOUND == hr)
232 {
233 OsExitOnRootFailure(hr = E_NOTIMPL, "Failed to load ntdll.dll");
234 }
235 OsExitOnFailure(hr, "Failed to load ntdll.dll.");
236
237 pfnRtlGetVersion = reinterpret_cast<PFN_RTL_GET_VERSION>(::GetProcAddress(hNtdll, "RtlGetVersion"));
238 OsExitOnNullWithLastError(pfnRtlGetVersion, hr, "Failed to locate RtlGetVersion.");
239
240 hr = static_cast<HRESULT>(pfnRtlGetVersion(&vovix));
241
242LExit:
243 memcpy(pOvix, &vovix, sizeof(RTL_OSVERSIONINFOEXW));
244
245 if (hNtdll)
246 {
247 ::FreeLibrary(hNtdll);
248 }
249
250 return hr;
251}
diff --git a/src/libs/dutil/WixToolset.DUtil/packages.config b/src/libs/dutil/WixToolset.DUtil/packages.config
new file mode 100644
index 00000000..5bbcd994
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/packages.config
@@ -0,0 +1,7 @@
1<?xml version="1.0" encoding="utf-8"?>
2<packages>
3 <package id="Microsoft.Build.Tasks.Git" version="1.0.0" targetFramework="native" developmentDependency="true" />
4 <package id="Microsoft.SourceLink.Common" version="1.0.0" targetFramework="native" developmentDependency="true" />
5 <package id="Microsoft.SourceLink.GitHub" version="1.0.0" targetFramework="native" developmentDependency="true" />
6 <package id="Nerdbank.GitVersioning" version="3.3.37" targetFramework="native" developmentDependency="true" />
7</packages> \ No newline at end of file
diff --git a/src/libs/dutil/WixToolset.DUtil/path2utl.cpp b/src/libs/dutil/WixToolset.DUtil/path2utl.cpp
new file mode 100644
index 00000000..ff3a946d
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/path2utl.cpp
@@ -0,0 +1,104 @@
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// Exit macros
7#define PathExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__)
8#define PathExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__)
9#define PathExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__)
10#define PathExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__)
11#define PathExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__)
12#define PathExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__)
13#define PathExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_PATHUTIL, p, x, e, s, __VA_ARGS__)
14#define PathExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_PATHUTIL, p, x, s, __VA_ARGS__)
15#define PathExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_PATHUTIL, p, x, e, s, __VA_ARGS__)
16#define PathExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_PATHUTIL, p, x, s, __VA_ARGS__)
17#define PathExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_PATHUTIL, e, x, s, __VA_ARGS__)
18#define PathExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_PATHUTIL, g, x, s, __VA_ARGS__)
19
20
21DAPI_(HRESULT) PathCanonicalizePath(
22 __in_z LPCWSTR wzPath,
23 __deref_out_z LPWSTR* psczCanonicalized
24 )
25{
26 HRESULT hr = S_OK;
27 int cch = MAX_PATH + 1;
28
29 hr = StrAlloc(psczCanonicalized, cch);
30 PathExitOnFailure(hr, "Failed to allocate string for the canonicalized path.");
31
32 if (::PathCanonicalizeW(*psczCanonicalized, wzPath))
33 {
34 hr = S_OK;
35 }
36 else
37 {
38 ExitFunctionWithLastError(hr);
39 }
40
41LExit:
42 return hr;
43}
44
45DAPI_(HRESULT) PathDirectoryContainsPath(
46 __in_z LPCWSTR wzDirectory,
47 __in_z LPCWSTR wzPath
48 )
49{
50 HRESULT hr = S_OK;
51 LPWSTR sczPath = NULL;
52 LPWSTR sczDirectory = NULL;
53 LPWSTR sczOriginalPath = NULL;
54 LPWSTR sczOriginalDirectory = NULL;
55
56 hr = PathCanonicalizePath(wzPath, &sczOriginalPath);
57 PathExitOnFailure(hr, "Failed to canonicalize the path.");
58
59 hr = PathCanonicalizePath(wzDirectory, &sczOriginalDirectory);
60 PathExitOnFailure(hr, "Failed to canonicalize the directory.");
61
62 if (!sczOriginalPath || !*sczOriginalPath)
63 {
64 ExitFunction1(hr = S_FALSE);
65 }
66 if (!sczOriginalDirectory || !*sczOriginalDirectory)
67 {
68 ExitFunction1(hr = S_FALSE);
69 }
70
71 sczPath = sczOriginalPath;
72 sczDirectory = sczOriginalDirectory;
73
74 for (; *sczDirectory;)
75 {
76 if (!*sczPath)
77 {
78 ExitFunction1(hr = S_FALSE);
79 }
80
81 if (CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, sczDirectory, 1, sczPath, 1))
82 {
83 ExitFunction1(hr = S_FALSE);
84 }
85
86 ++sczDirectory;
87 ++sczPath;
88 }
89
90 --sczDirectory;
91 if (('\\' == *sczDirectory && *sczPath) || '\\' == *sczPath)
92 {
93 hr = S_OK;
94 }
95 else
96 {
97 hr = S_FALSE;
98 }
99
100LExit:
101 ReleaseStr(sczOriginalPath);
102 ReleaseStr(sczOriginalDirectory);
103 return hr;
104}
diff --git a/src/libs/dutil/WixToolset.DUtil/pathutil.cpp b/src/libs/dutil/WixToolset.DUtil/pathutil.cpp
new file mode 100644
index 00000000..7c3cfe06
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/pathutil.cpp
@@ -0,0 +1,1083 @@
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// Exit macros
7#define PathExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__)
8#define PathExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__)
9#define PathExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__)
10#define PathExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__)
11#define PathExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__)
12#define PathExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__)
13#define PathExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_PATHUTIL, p, x, e, s, __VA_ARGS__)
14#define PathExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_PATHUTIL, p, x, s, __VA_ARGS__)
15#define PathExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_PATHUTIL, p, x, e, s, __VA_ARGS__)
16#define PathExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_PATHUTIL, p, x, s, __VA_ARGS__)
17#define PathExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_PATHUTIL, e, x, s, __VA_ARGS__)
18#define PathExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_PATHUTIL, g, x, s, __VA_ARGS__)
19
20#define PATH_GOOD_ENOUGH 64
21
22
23DAPI_(HRESULT) PathCommandLineAppend(
24 __deref_inout_z LPWSTR* psczCommandLine,
25 __in_z LPCWSTR wzArgument
26 )
27{
28 HRESULT hr = S_OK;
29 LPWSTR sczQuotedArg = NULL;
30 BOOL fRequiresQuoting = FALSE;
31 DWORD dwMaxEscapedSize = 0;
32
33 // Loop through the argument determining if it needs to be quoted and what the maximum
34 // size would be if there are escape characters required.
35 for (LPCWSTR pwz = wzArgument; *pwz; ++pwz)
36 {
37 // Arguments with whitespace need quoting.
38 if (L' ' == *pwz || L'\t' == *pwz || L'\n' == *pwz || L'\v' == *pwz)
39 {
40 fRequiresQuoting = TRUE;
41 }
42 else if (L'"' == *pwz) // quotes need quoting and sometimes escaping.
43 {
44 fRequiresQuoting = TRUE;
45 ++dwMaxEscapedSize;
46 }
47 else if (L'\\' == *pwz) // some backslashes need escaping, so we'll count them all to make sure there is room.
48 {
49 ++dwMaxEscapedSize;
50 }
51
52 ++dwMaxEscapedSize;
53 }
54
55 // If we found anything in the argument that requires our argument to be quoted
56 if (fRequiresQuoting)
57 {
58 hr = StrAlloc(&sczQuotedArg, dwMaxEscapedSize + 3); // plus three for the start and end quote plus null terminator.
59 PathExitOnFailure(hr, "Failed to allocate argument to be quoted.");
60
61 LPCWSTR pwz = wzArgument;
62 LPWSTR pwzQuoted = sczQuotedArg;
63
64 *pwzQuoted = L'"';
65 ++pwzQuoted;
66 while (*pwz)
67 {
68 DWORD dwBackslashes = 0;
69 while (L'\\' == *pwz)
70 {
71 ++dwBackslashes;
72 ++pwz;
73 }
74
75 // Escape all backslashes at the end of the string.
76 if (!*pwz)
77 {
78 dwBackslashes *= 2;
79 }
80 else if (L'"' == *pwz) // escape all backslashes before the quote and escape the quote itself.
81 {
82 dwBackslashes = dwBackslashes * 2 + 1;
83 }
84 // the backslashes don't have to be escaped.
85
86 // Add the appropriate number of backslashes
87 for (DWORD i = 0; i < dwBackslashes; ++i)
88 {
89 *pwzQuoted = L'\\';
90 ++pwzQuoted;
91 }
92
93 // If there is a character, add it after all the escaped backslashes
94 if (*pwz)
95 {
96 *pwzQuoted = *pwz;
97 ++pwz;
98 ++pwzQuoted;
99 }
100 }
101
102 *pwzQuoted = L'"';
103 ++pwzQuoted;
104 *pwzQuoted = L'\0'; // ensure the arg is null terminated.
105 }
106
107 // If there is already data in the command line, append a space before appending the
108 // argument.
109 if (*psczCommandLine && **psczCommandLine)
110 {
111 hr = StrAllocConcat(psczCommandLine, L" ", 0);
112 PathExitOnFailure(hr, "Failed to append space to command line with existing data.");
113 }
114
115 hr = StrAllocConcat(psczCommandLine, sczQuotedArg ? sczQuotedArg : wzArgument, 0);
116 PathExitOnFailure(hr, "Failed to copy command line argument.");
117
118LExit:
119 ReleaseStr(sczQuotedArg);
120
121 return hr;
122}
123
124
125DAPI_(LPWSTR) PathFile(
126 __in_z LPCWSTR wzPath
127 )
128{
129 if (!wzPath)
130 {
131 return NULL;
132 }
133
134 LPWSTR wzFile = const_cast<LPWSTR>(wzPath);
135 for (LPWSTR wz = wzFile; *wz; ++wz)
136 {
137 // valid delineators
138 // \ => Windows path
139 // / => unix and URL path
140 // : => relative path from mapped root
141 if (L'\\' == *wz || L'/' == *wz || (L':' == *wz && wz == wzPath + 1))
142 {
143 wzFile = wz + 1;
144 }
145 }
146
147 return wzFile;
148}
149
150
151DAPI_(LPCWSTR) PathExtension(
152 __in_z LPCWSTR wzPath
153 )
154{
155 if (!wzPath)
156 {
157 return NULL;
158 }
159
160 // Find the last dot in the last thing that could be a file.
161 LPCWSTR wzExtension = NULL;
162 for (LPCWSTR wz = wzPath; *wz; ++wz)
163 {
164 if (L'\\' == *wz || L'/' == *wz || L':' == *wz)
165 {
166 wzExtension = NULL;
167 }
168 else if (L'.' == *wz)
169 {
170 wzExtension = wz;
171 }
172 }
173
174 return wzExtension;
175}
176
177
178DAPI_(HRESULT) PathGetDirectory(
179 __in_z LPCWSTR wzPath,
180 __out_z LPWSTR *psczDirectory
181 )
182{
183 HRESULT hr = S_OK;
184 size_t cchDirectory = SIZE_T_MAX;
185
186 for (LPCWSTR wz = wzPath; *wz; ++wz)
187 {
188 // valid delineators:
189 // \ => Windows path
190 // / => unix and URL path
191 // : => relative path from mapped root
192 if (L'\\' == *wz || L'/' == *wz || (L':' == *wz && wz == wzPath + 1))
193 {
194 cchDirectory = static_cast<size_t>(wz - wzPath) + 1;
195 }
196 }
197
198 if (SIZE_T_MAX == cchDirectory)
199 {
200 // we were given just a file name, so there's no directory available
201 return S_FALSE;
202 }
203
204 if (wzPath[0] == L'\"')
205 {
206 ++wzPath;
207 --cchDirectory;
208 }
209
210 hr = StrAllocString(psczDirectory, wzPath, cchDirectory);
211 PathExitOnFailure(hr, "Failed to copy directory.");
212
213LExit:
214 return hr;
215}
216
217
218DAPI_(HRESULT) PathGetParentPath(
219 __in_z LPCWSTR wzPath,
220 __out_z LPWSTR *psczParent
221 )
222{
223 HRESULT hr = S_OK;
224 LPCWSTR wzParent = NULL;
225
226 for (LPCWSTR wz = wzPath; *wz; ++wz)
227 {
228 if (wz[1] && (L'\\' == *wz || L'/' == *wz))
229 {
230 wzParent = wz;
231 }
232 }
233
234 if (wzParent)
235 {
236 size_t cchPath = static_cast<size_t>(wzParent - wzPath) + 1;
237
238 hr = StrAllocString(psczParent, wzPath, cchPath);
239 PathExitOnFailure(hr, "Failed to copy directory.");
240 }
241 else
242 {
243 ReleaseNullStr(psczParent);
244 }
245
246LExit:
247 return hr;
248}
249
250
251DAPI_(HRESULT) PathExpand(
252 __out LPWSTR *psczFullPath,
253 __in_z LPCWSTR wzRelativePath,
254 __in DWORD dwResolveFlags
255 )
256{
257 Assert(wzRelativePath && *wzRelativePath);
258
259 HRESULT hr = S_OK;
260 DWORD cch = 0;
261 LPWSTR sczExpandedPath = NULL;
262 DWORD cchExpandedPath = 0;
263 SIZE_T cbSize = 0;
264
265 LPWSTR sczFullPath = NULL;
266
267 //
268 // First, expand any environment variables.
269 //
270 if (dwResolveFlags & PATH_EXPAND_ENVIRONMENT)
271 {
272 cchExpandedPath = PATH_GOOD_ENOUGH;
273
274 hr = StrAlloc(&sczExpandedPath, cchExpandedPath);
275 PathExitOnFailure(hr, "Failed to allocate space for expanded path.");
276
277 cch = ::ExpandEnvironmentStringsW(wzRelativePath, sczExpandedPath, cchExpandedPath);
278 if (0 == cch)
279 {
280 PathExitWithLastError(hr, "Failed to expand environment variables in string: %ls", wzRelativePath);
281 }
282 else if (cchExpandedPath < cch)
283 {
284 cchExpandedPath = cch;
285 hr = StrAlloc(&sczExpandedPath, cchExpandedPath);
286 PathExitOnFailure(hr, "Failed to re-allocate more space for expanded path.");
287
288 cch = ::ExpandEnvironmentStringsW(wzRelativePath, sczExpandedPath, cchExpandedPath);
289 if (0 == cch)
290 {
291 PathExitWithLastError(hr, "Failed to expand environment variables in string: %ls", wzRelativePath);
292 }
293 else if (cchExpandedPath < cch)
294 {
295 hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
296 PathExitOnRootFailure(hr, "Failed to allocate buffer for expanded path.");
297 }
298 }
299
300 if (MAX_PATH < cch)
301 {
302 hr = PathPrefix(&sczExpandedPath); // ignore invald arg from path prefix because this may not be a complete path yet
303 if (E_INVALIDARG == hr)
304 {
305 hr = S_OK;
306 }
307 PathExitOnFailure(hr, "Failed to prefix long path after expanding environment variables.");
308
309 hr = StrMaxLength(sczExpandedPath, &cbSize);
310 PathExitOnFailure(hr, "Failed to get max length of expanded path.");
311
312 cchExpandedPath = (DWORD)min(DWORD_MAX, cbSize);
313 }
314 }
315
316 //
317 // Second, get the full path.
318 //
319 if (dwResolveFlags & PATH_EXPAND_FULLPATH)
320 {
321 LPWSTR wzFileName = NULL;
322 LPCWSTR wzPath = sczExpandedPath ? sczExpandedPath : wzRelativePath;
323 DWORD cchFullPath = max(PATH_GOOD_ENOUGH, cchExpandedPath);
324
325 hr = StrAlloc(&sczFullPath, cchFullPath);
326 PathExitOnFailure(hr, "Failed to allocate space for full path.");
327
328 cch = ::GetFullPathNameW(wzPath, cchFullPath, sczFullPath, &wzFileName);
329 if (0 == cch)
330 {
331 PathExitWithLastError(hr, "Failed to get full path for string: %ls", wzPath);
332 }
333 else if (cchFullPath < cch)
334 {
335 cchFullPath = cch < MAX_PATH ? cch : cch + 7; // ensure space for "\\?\UNC" prefix if needed
336 hr = StrAlloc(&sczFullPath, cchFullPath);
337 PathExitOnFailure(hr, "Failed to re-allocate more space for full path.");
338
339 cch = ::GetFullPathNameW(wzPath, cchFullPath, sczFullPath, &wzFileName);
340 if (0 == cch)
341 {
342 PathExitWithLastError(hr, "Failed to get full path for string: %ls", wzPath);
343 }
344 else if (cchFullPath < cch)
345 {
346 hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
347 PathExitOnRootFailure(hr, "Failed to allocate buffer for full path.");
348 }
349 }
350
351 if (MAX_PATH < cch)
352 {
353 hr = PathPrefix(&sczFullPath);
354 PathExitOnFailure(hr, "Failed to prefix long path after expanding.");
355 }
356 }
357 else
358 {
359 sczFullPath = sczExpandedPath;
360 sczExpandedPath = NULL;
361 }
362
363 hr = StrAllocString(psczFullPath, sczFullPath? sczFullPath : wzRelativePath, 0);
364 PathExitOnFailure(hr, "Failed to copy relative path into full path.");
365
366LExit:
367 ReleaseStr(sczFullPath);
368 ReleaseStr(sczExpandedPath);
369
370 return hr;
371}
372
373
374DAPI_(HRESULT) PathPrefix(
375 __inout LPWSTR *psczFullPath
376 )
377{
378 Assert(psczFullPath && *psczFullPath);
379
380 HRESULT hr = S_OK;
381 LPWSTR wzFullPath = *psczFullPath;
382 SIZE_T cbFullPath = 0;
383
384 if (((L'a' <= wzFullPath[0] && L'z' >= wzFullPath[0]) ||
385 (L'A' <= wzFullPath[0] && L'Z' >= wzFullPath[0])) &&
386 L':' == wzFullPath[1] &&
387 L'\\' == wzFullPath[2]) // normal path
388 {
389 hr = StrAllocPrefix(psczFullPath, L"\\\\?\\", 4);
390 PathExitOnFailure(hr, "Failed to add prefix to file path.");
391 }
392 else if (L'\\' == wzFullPath[0] && L'\\' == wzFullPath[1]) // UNC
393 {
394 // ensure that we're not already prefixed
395 if (!(L'?' == wzFullPath[2] && L'\\' == wzFullPath[3]))
396 {
397 hr = StrSize(*psczFullPath, &cbFullPath);
398 PathExitOnFailure(hr, "Failed to get size of full path.");
399
400 memmove_s(wzFullPath, cbFullPath, wzFullPath + 1, cbFullPath - sizeof(WCHAR));
401
402 hr = StrAllocPrefix(psczFullPath, L"\\\\?\\UNC", 7);
403 PathExitOnFailure(hr, "Failed to add prefix to UNC path.");
404 }
405 }
406 else
407 {
408 hr = E_INVALIDARG;
409 PathExitOnFailure(hr, "Invalid path provided to prefix: %ls.", wzFullPath);
410 }
411
412LExit:
413 return hr;
414}
415
416
417DAPI_(HRESULT) PathFixedBackslashTerminate(
418 __inout_ecount_z(cchPath) LPWSTR wzPath,
419 __in SIZE_T cchPath
420 )
421{
422 HRESULT hr = S_OK;
423 size_t cchLength = 0;
424
425 hr = ::StringCchLengthW(wzPath, cchPath, &cchLength);
426 PathExitOnFailure(hr, "Failed to get length of path.");
427
428 if (cchLength >= cchPath)
429 {
430 hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
431 }
432 else if (L'\\' != wzPath[cchLength - 1])
433 {
434 wzPath[cchLength] = L'\\';
435 wzPath[cchLength + 1] = L'\0';
436 }
437
438LExit:
439 return hr;
440}
441
442
443DAPI_(HRESULT) PathBackslashTerminate(
444 __inout LPWSTR* psczPath
445 )
446{
447 Assert(psczPath && *psczPath);
448
449 HRESULT hr = S_OK;
450 SIZE_T cchPath = 0;
451 size_t cchLength = 0;
452
453 hr = StrMaxLength(*psczPath, &cchPath);
454 PathExitOnFailure(hr, "Failed to get size of path string.");
455
456 hr = ::StringCchLengthW(*psczPath, cchPath, &cchLength);
457 PathExitOnFailure(hr, "Failed to get length of path.");
458
459 if (L'\\' != (*psczPath)[cchLength - 1])
460 {
461 hr = StrAllocConcat(psczPath, L"\\", 1);
462 PathExitOnFailure(hr, "Failed to concat backslash onto string.");
463 }
464
465LExit:
466 return hr;
467}
468
469
470DAPI_(HRESULT) PathForCurrentProcess(
471 __inout LPWSTR *psczFullPath,
472 __in_opt HMODULE hModule
473 )
474{
475 HRESULT hr = S_OK;
476 DWORD cch = MAX_PATH;
477
478 do
479 {
480 hr = StrAlloc(psczFullPath, cch);
481 PathExitOnFailure(hr, "Failed to allocate string for module path.");
482
483 DWORD cchRequired = ::GetModuleFileNameW(hModule, *psczFullPath, cch);
484 if (0 == cchRequired)
485 {
486 PathExitWithLastError(hr, "Failed to get path for executing process.");
487 }
488 else if (cchRequired == cch)
489 {
490 cch = cchRequired + 1;
491 hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
492 }
493 else
494 {
495 hr = S_OK;
496 }
497 } while (HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) == hr);
498
499LExit:
500 return hr;
501}
502
503
504DAPI_(HRESULT) PathRelativeToModule(
505 __inout LPWSTR *psczFullPath,
506 __in_opt LPCWSTR wzFileName,
507 __in_opt HMODULE hModule
508 )
509{
510 HRESULT hr = PathForCurrentProcess(psczFullPath, hModule);
511 PathExitOnFailure(hr, "Failed to get current module path.");
512
513 hr = PathGetDirectory(*psczFullPath, psczFullPath);
514 PathExitOnFailure(hr, "Failed to get current module directory.");
515
516 if (wzFileName)
517 {
518 hr = PathConcat(*psczFullPath, wzFileName, psczFullPath);
519 PathExitOnFailure(hr, "Failed to append filename.");
520 }
521
522LExit:
523 return hr;
524}
525
526
527DAPI_(HRESULT) PathCreateTempFile(
528 __in_opt LPCWSTR wzDirectory,
529 __in_opt __format_string LPCWSTR wzFileNameTemplate,
530 __in DWORD dwUniqueCount,
531 __in DWORD dwFileAttributes,
532 __out_opt LPWSTR* psczTempFile,
533 __out_opt HANDLE* phTempFile
534 )
535{
536 AssertSz(0 < dwUniqueCount, "Must specify a non-zero unique count.");
537
538 HRESULT hr = S_OK;
539
540 LPWSTR sczTempPath = NULL;
541 DWORD cchTempPath = MAX_PATH;
542
543 HANDLE hTempFile = INVALID_HANDLE_VALUE;
544 LPWSTR scz = NULL;
545 LPWSTR sczTempFile = NULL;
546
547 if (wzDirectory && *wzDirectory)
548 {
549 hr = StrAllocString(&sczTempPath, wzDirectory, 0);
550 PathExitOnFailure(hr, "Failed to copy temp path.");
551 }
552 else
553 {
554 hr = StrAlloc(&sczTempPath, cchTempPath);
555 PathExitOnFailure(hr, "Failed to allocate memory for the temp path.");
556
557 if (!::GetTempPathW(cchTempPath, sczTempPath))
558 {
559 PathExitWithLastError(hr, "Failed to get temp path.");
560 }
561 }
562
563 if (wzFileNameTemplate && *wzFileNameTemplate)
564 {
565 for (DWORD i = 1; i <= dwUniqueCount && INVALID_HANDLE_VALUE == hTempFile; ++i)
566 {
567 hr = StrAllocFormatted(&scz, wzFileNameTemplate, i);
568 PathExitOnFailure(hr, "Failed to allocate memory for file template.");
569
570 hr = StrAllocFormatted(&sczTempFile, L"%s%s", sczTempPath, scz);
571 PathExitOnFailure(hr, "Failed to allocate temp file name.");
572
573 hTempFile = ::CreateFileW(sczTempFile, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, CREATE_NEW, dwFileAttributes, NULL);
574 if (INVALID_HANDLE_VALUE == hTempFile)
575 {
576 // if the file already exists, just try again
577 hr = HRESULT_FROM_WIN32(::GetLastError());
578 if (HRESULT_FROM_WIN32(ERROR_FILE_EXISTS) == hr)
579 {
580 hr = S_OK;
581 }
582 PathExitOnFailure(hr, "Failed to create file: %ls", sczTempFile);
583 }
584 }
585 }
586
587 // If we were not able to or we did not try to create a temp file, ask
588 // the system to provide us a temp file using its built-in mechanism.
589 if (INVALID_HANDLE_VALUE == hTempFile)
590 {
591 hr = StrAlloc(&sczTempFile, MAX_PATH);
592 PathExitOnFailure(hr, "Failed to allocate memory for the temp path");
593
594 if (!::GetTempFileNameW(sczTempPath, L"TMP", 0, sczTempFile))
595 {
596 PathExitWithLastError(hr, "Failed to create new temp file name.");
597 }
598
599 hTempFile = ::CreateFileW(sczTempFile, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, dwFileAttributes, NULL);
600 if (INVALID_HANDLE_VALUE == hTempFile)
601 {
602 PathExitWithLastError(hr, "Failed to open new temp file: %ls", sczTempFile);
603 }
604 }
605
606 // If the caller wanted the temp file name or handle, return them here.
607 if (psczTempFile)
608 {
609 hr = StrAllocString(psczTempFile, sczTempFile, 0);
610 PathExitOnFailure(hr, "Failed to copy temp file string.");
611 }
612
613 if (phTempFile)
614 {
615 *phTempFile = hTempFile;
616 hTempFile = INVALID_HANDLE_VALUE;
617 }
618
619LExit:
620 if (INVALID_HANDLE_VALUE != hTempFile)
621 {
622 ::CloseHandle(hTempFile);
623 }
624
625 ReleaseStr(scz);
626 ReleaseStr(sczTempFile);
627 ReleaseStr(sczTempPath);
628
629 return hr;
630}
631
632
633DAPI_(HRESULT) PathCreateTimeBasedTempFile(
634 __in_z_opt LPCWSTR wzDirectory,
635 __in_z LPCWSTR wzPrefix,
636 __in_z_opt LPCWSTR wzPostfix,
637 __in_z LPCWSTR wzExtension,
638 __deref_opt_out_z LPWSTR* psczTempFile,
639 __out_opt HANDLE* phTempFile
640 )
641{
642 HRESULT hr = S_OK;
643 BOOL fRetry = FALSE;
644 WCHAR wzTempPath[MAX_PATH] = { };
645 LPWSTR sczPrefix = NULL;
646 LPWSTR sczPrefixFolder = NULL;
647 SYSTEMTIME time = { };
648
649 LPWSTR sczTempPath = NULL;
650 HANDLE hTempFile = INVALID_HANDLE_VALUE;
651 DWORD dwAttempts = 0;
652
653 if (wzDirectory && *wzDirectory)
654 {
655 hr = PathConcat(wzDirectory, wzPrefix, &sczPrefix);
656 PathExitOnFailure(hr, "Failed to combine directory and log prefix.");
657 }
658 else
659 {
660 if (!::GetTempPathW(countof(wzTempPath), wzTempPath))
661 {
662 PathExitWithLastError(hr, "Failed to get temp folder.");
663 }
664
665 hr = PathConcat(wzTempPath, wzPrefix, &sczPrefix);
666 PathExitOnFailure(hr, "Failed to concatenate the temp folder and log prefix.");
667 }
668
669 hr = PathGetDirectory(sczPrefix, &sczPrefixFolder);
670 if (S_OK == hr)
671 {
672 hr = DirEnsureExists(sczPrefixFolder, NULL);
673 PathExitOnFailure(hr, "Failed to ensure temp file path exists: %ls", sczPrefixFolder);
674 }
675
676 if (!wzPostfix)
677 {
678 wzPostfix = L"";
679 }
680
681 do
682 {
683 fRetry = FALSE;
684 ++dwAttempts;
685
686 ::GetLocalTime(&time);
687
688 // Log format: pre YYYY MM dd hh mm ss post ext
689 hr = StrAllocFormatted(&sczTempPath, L"%ls_%04u%02u%02u%02u%02u%02u%ls%ls%ls", sczPrefix, time.wYear, time.wMonth, time.wDay, time.wHour, time.wMinute, time.wSecond, wzPostfix, L'.' == *wzExtension ? L"" : L".", wzExtension);
690 PathExitOnFailure(hr, "failed to allocate memory for the temp path");
691
692 hTempFile = ::CreateFileW(sczTempPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
693 if (INVALID_HANDLE_VALUE == hTempFile)
694 {
695 // If the file already exists, just try again.
696 DWORD er = ::GetLastError();
697 if (ERROR_FILE_EXISTS == er || ERROR_ACCESS_DENIED == er)
698 {
699 ::Sleep(100);
700
701 if (10 > dwAttempts)
702 {
703 er = ERROR_SUCCESS;
704 fRetry = TRUE;
705 }
706 }
707
708 hr = HRESULT_FROM_WIN32(er);
709 PathExitOnFailureDebugTrace(hr, "Failed to create temp file: %ls", sczTempPath);
710 }
711 } while (fRetry);
712
713 if (psczTempFile)
714 {
715 hr = StrAllocString(psczTempFile, sczTempPath, 0);
716 PathExitOnFailure(hr, "Failed to copy temp path to return.");
717 }
718
719 if (phTempFile)
720 {
721 *phTempFile = hTempFile;
722 hTempFile = INVALID_HANDLE_VALUE;
723 }
724
725LExit:
726 ReleaseFile(hTempFile);
727 ReleaseStr(sczTempPath);
728 ReleaseStr(sczPrefixFolder);
729 ReleaseStr(sczPrefix);
730
731 return hr;
732}
733
734
735DAPI_(HRESULT) PathCreateTempDirectory(
736 __in_opt LPCWSTR wzDirectory,
737 __in __format_string LPCWSTR wzDirectoryNameTemplate,
738 __in DWORD dwUniqueCount,
739 __out LPWSTR* psczTempDirectory
740 )
741{
742 AssertSz(wzDirectoryNameTemplate && *wzDirectoryNameTemplate, "DirectoryNameTemplate must be specified.");
743 AssertSz(0 < dwUniqueCount, "Must specify a non-zero unique count.");
744
745 HRESULT hr = S_OK;
746
747 LPWSTR sczTempPath = NULL;
748 DWORD cchTempPath = MAX_PATH;
749
750 LPWSTR scz = NULL;
751
752 if (wzDirectory && *wzDirectory)
753 {
754 hr = StrAllocString(&sczTempPath, wzDirectory, 0);
755 PathExitOnFailure(hr, "Failed to copy temp path.");
756
757 hr = PathBackslashTerminate(&sczTempPath);
758 PathExitOnFailure(hr, "Failed to ensure path ends in backslash: %ls", wzDirectory);
759 }
760 else
761 {
762 hr = StrAlloc(&sczTempPath, cchTempPath);
763 PathExitOnFailure(hr, "Failed to allocate memory for the temp path.");
764
765 if (!::GetTempPathW(cchTempPath, sczTempPath))
766 {
767 PathExitWithLastError(hr, "Failed to get temp path.");
768 }
769 }
770
771 for (DWORD i = 1; i <= dwUniqueCount; ++i)
772 {
773 hr = StrAllocFormatted(&scz, wzDirectoryNameTemplate, i);
774 PathExitOnFailure(hr, "Failed to allocate memory for directory name template.");
775
776 hr = StrAllocFormatted(psczTempDirectory, L"%s%s", sczTempPath, scz);
777 PathExitOnFailure(hr, "Failed to allocate temp directory name.");
778
779 if (!::CreateDirectoryW(*psczTempDirectory, NULL))
780 {
781 DWORD er = ::GetLastError();
782 if (ERROR_ALREADY_EXISTS == er)
783 {
784 hr = HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS);
785 continue;
786 }
787 else if (ERROR_PATH_NOT_FOUND == er)
788 {
789 hr = DirEnsureExists(*psczTempDirectory, NULL);
790 break;
791 }
792 else
793 {
794 hr = HRESULT_FROM_WIN32(er);
795 break;
796 }
797 }
798 else
799 {
800 hr = S_OK;
801 break;
802 }
803 }
804 PathExitOnFailure(hr, "Failed to create temp directory.");
805
806 hr = PathBackslashTerminate(psczTempDirectory);
807 PathExitOnFailure(hr, "Failed to ensure temp directory is backslash terminated.");
808
809LExit:
810 ReleaseStr(scz);
811 ReleaseStr(sczTempPath);
812
813 return hr;
814}
815
816
817DAPI_(HRESULT) PathGetKnownFolder(
818 __in int csidl,
819 __out LPWSTR* psczKnownFolder
820 )
821{
822 HRESULT hr = S_OK;
823
824 hr = StrAlloc(psczKnownFolder, MAX_PATH);
825 PathExitOnFailure(hr, "Failed to allocate memory for known folder.");
826
827 hr = ::SHGetFolderPathW(NULL, csidl, NULL, SHGFP_TYPE_CURRENT, *psczKnownFolder);
828 PathExitOnFailure(hr, "Failed to get known folder path.");
829
830 hr = PathBackslashTerminate(psczKnownFolder);
831 PathExitOnFailure(hr, "Failed to ensure known folder path is backslash terminated.");
832
833LExit:
834 return hr;
835}
836
837
838DAPI_(BOOL) PathIsAbsolute(
839 __in_z LPCWSTR wzPath
840 )
841{
842 return wzPath && wzPath[0] && wzPath[1] && (wzPath[0] == L'\\') || (wzPath[1] == L':');
843}
844
845
846DAPI_(HRESULT) PathConcat(
847 __in_opt LPCWSTR wzPath1,
848 __in_opt LPCWSTR wzPath2,
849 __deref_out_z LPWSTR* psczCombined
850 )
851{
852 return PathConcatCch(wzPath1, 0, wzPath2, 0, psczCombined);
853}
854
855
856DAPI_(HRESULT) PathConcatCch(
857 __in_opt LPCWSTR wzPath1,
858 __in SIZE_T cchPath1,
859 __in_opt LPCWSTR wzPath2,
860 __in SIZE_T cchPath2,
861 __deref_out_z LPWSTR* psczCombined
862 )
863{
864 HRESULT hr = S_OK;
865
866 if (!wzPath2 || !*wzPath2)
867 {
868 hr = StrAllocString(psczCombined, wzPath1, cchPath1);
869 PathExitOnFailure(hr, "Failed to copy just path1 to output.");
870 }
871 else if (!wzPath1 || !*wzPath1 || PathIsAbsolute(wzPath2))
872 {
873 hr = StrAllocString(psczCombined, wzPath2, cchPath2);
874 PathExitOnFailure(hr, "Failed to copy just path2 to output.");
875 }
876 else
877 {
878 hr = StrAllocString(psczCombined, wzPath1, cchPath1);
879 PathExitOnFailure(hr, "Failed to copy path1 to output.");
880
881 hr = PathBackslashTerminate(psczCombined);
882 PathExitOnFailure(hr, "Failed to backslashify.");
883
884 hr = StrAllocConcat(psczCombined, wzPath2, cchPath2);
885 PathExitOnFailure(hr, "Failed to append path2 to output.");
886 }
887
888LExit:
889 return hr;
890}
891
892
893DAPI_(HRESULT) PathEnsureQuoted(
894 __inout LPWSTR* ppszPath,
895 __in BOOL fDirectory
896 )
897{
898 Assert(ppszPath && *ppszPath);
899
900 HRESULT hr = S_OK;
901 size_t cchPath = 0;
902
903 hr = ::StringCchLengthW(*ppszPath, STRSAFE_MAX_CCH, &cchPath);
904 PathExitOnFailure(hr, "Failed to get the length of the path.");
905
906 // Handle simple special cases.
907 if (0 == cchPath || (1 == cchPath && L'"' == (*ppszPath)[0]))
908 {
909 hr = StrAllocString(ppszPath, L"\"\"", 2);
910 PathExitOnFailure(hr, "Failed to allocate a quoted empty string.");
911
912 ExitFunction();
913 }
914
915 if (L'"' != (*ppszPath)[0])
916 {
917 hr = StrAllocPrefix(ppszPath, L"\"", 1);
918 PathExitOnFailure(hr, "Failed to allocate an opening quote.");
919
920 // Add a char for the opening quote.
921 ++cchPath;
922 }
923
924 if (L'"' != (*ppszPath)[cchPath - 1])
925 {
926 hr = StrAllocConcat(ppszPath, L"\"", 1);
927 PathExitOnFailure(hr, "Failed to allocate a closing quote.");
928
929 // Add a char for the closing quote.
930 ++cchPath;
931 }
932
933 if (fDirectory)
934 {
935 if (L'\\' != (*ppszPath)[cchPath - 2])
936 {
937 // Change the last char to a backslash and re-append the closing quote.
938 (*ppszPath)[cchPath - 1] = L'\\';
939
940 hr = StrAllocConcat(ppszPath, L"\"", 1);
941 PathExitOnFailure(hr, "Failed to allocate another closing quote after the backslash.");
942 }
943 }
944
945LExit:
946
947 return hr;
948}
949
950
951DAPI_(HRESULT) PathCompare(
952 __in_z LPCWSTR wzPath1,
953 __in_z LPCWSTR wzPath2,
954 __out int* pnResult
955 )
956{
957 HRESULT hr = S_OK;
958 LPWSTR sczPath1 = NULL;
959 LPWSTR sczPath2 = NULL;
960
961 hr = PathExpand(&sczPath1, wzPath1, PATH_EXPAND_ENVIRONMENT | PATH_EXPAND_FULLPATH);
962 PathExitOnFailure(hr, "Failed to expand path1.");
963
964 hr = PathExpand(&sczPath2, wzPath2, PATH_EXPAND_ENVIRONMENT | PATH_EXPAND_FULLPATH);
965 PathExitOnFailure(hr, "Failed to expand path2.");
966
967 *pnResult = ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, sczPath1, -1, sczPath2, -1);
968
969LExit:
970 ReleaseStr(sczPath2);
971 ReleaseStr(sczPath1);
972
973 return hr;
974}
975
976
977DAPI_(HRESULT) PathCompress(
978 __in_z LPCWSTR wzPath
979 )
980{
981 HRESULT hr = S_OK;
982 HANDLE hPath = INVALID_HANDLE_VALUE;
983
984 hPath = ::CreateFileW(wzPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
985 if (INVALID_HANDLE_VALUE == hPath)
986 {
987 PathExitWithLastError(hr, "Failed to open path %ls for compression.", wzPath);
988 }
989
990 DWORD dwBytesReturned = 0;
991 USHORT usCompressionFormat = COMPRESSION_FORMAT_DEFAULT;
992 if (0 == ::DeviceIoControl(hPath, FSCTL_SET_COMPRESSION, &usCompressionFormat, sizeof(usCompressionFormat), NULL, 0, &dwBytesReturned, NULL))
993 {
994 // ignore compression attempts on file systems that don't support it
995 DWORD er = ::GetLastError();
996 if (ERROR_INVALID_FUNCTION != er)
997 {
998 PathExitOnWin32Error(er, hr, "Failed to set compression state for path %ls.", wzPath);
999 }
1000 }
1001
1002LExit:
1003 ReleaseFile(hPath);
1004
1005 return hr;
1006}
1007
1008DAPI_(HRESULT) PathGetHierarchyArray(
1009 __in_z LPCWSTR wzPath,
1010 __deref_inout_ecount_opt(*pcPathArray) LPWSTR **prgsczPathArray,
1011 __inout LPUINT pcPathArray
1012 )
1013{
1014 HRESULT hr = S_OK;
1015 LPWSTR sczPathCopy = NULL;
1016 LPWSTR sczNewPathCopy = NULL;
1017 DWORD cArraySpacesNeeded = 0;
1018 size_t cchPath = 0;
1019
1020 hr = ::StringCchLengthW(wzPath, STRSAFE_MAX_LENGTH, &cchPath);
1021 PathExitOnRootFailure(hr, "Failed to get string length of path: %ls", wzPath);
1022
1023 if (!cchPath)
1024 {
1025 ExitFunction1(hr = E_INVALIDARG);
1026 }
1027
1028 for (size_t i = 0; i < cchPath; ++i)
1029 {
1030 if (wzPath[i] == L'\\')
1031 {
1032 ++cArraySpacesNeeded;
1033 }
1034 }
1035
1036 if (wzPath[cchPath - 1] != L'\\')
1037 {
1038 ++cArraySpacesNeeded;
1039 }
1040
1041 // If it's a UNC path, cut off the first three paths, 2 because it starts with a double backslash, and another because the first ("\\servername\") isn't a path.
1042 if (wzPath[0] == L'\\' && wzPath[1] == L'\\')
1043 {
1044 cArraySpacesNeeded -= 3;
1045 }
1046
1047 Assert(cArraySpacesNeeded >= 1);
1048
1049 hr = MemEnsureArraySize(reinterpret_cast<void **>(prgsczPathArray), cArraySpacesNeeded, sizeof(LPWSTR), 0);
1050 PathExitOnFailure(hr, "Failed to allocate array of size %u for parent directories", cArraySpacesNeeded);
1051 *pcPathArray = cArraySpacesNeeded;
1052
1053 hr = StrAllocString(&sczPathCopy, wzPath, 0);
1054 PathExitOnFailure(hr, "Failed to allocate copy of original path");
1055
1056 for (DWORD i = 0; i < cArraySpacesNeeded; ++i)
1057 {
1058 hr = StrAllocString((*prgsczPathArray) + cArraySpacesNeeded - 1 - i, sczPathCopy, 0);
1059 PathExitOnFailure(hr, "Failed to copy path");
1060
1061 DWORD cchPathCopy = lstrlenW(sczPathCopy);
1062
1063 // If it ends in a backslash, it's a directory path, so cut off everything the last backslash before we get the directory portion of the path
1064 if (wzPath[cchPathCopy - 1] == L'\\')
1065 {
1066 sczPathCopy[cchPathCopy - 1] = L'\0';
1067 }
1068
1069 hr = PathGetDirectory(sczPathCopy, &sczNewPathCopy);
1070 PathExitOnFailure(hr, "Failed to get directory portion of path");
1071
1072 ReleaseStr(sczPathCopy);
1073 sczPathCopy = sczNewPathCopy;
1074 sczNewPathCopy = NULL;
1075 }
1076
1077 hr = S_OK;
1078
1079LExit:
1080 ReleaseStr(sczPathCopy);
1081
1082 return hr;
1083}
diff --git a/src/libs/dutil/WixToolset.DUtil/perfutil.cpp b/src/libs/dutil/WixToolset.DUtil/perfutil.cpp
new file mode 100644
index 00000000..bc138d34
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/perfutil.cpp
@@ -0,0 +1,82 @@
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// Exit macros
7#define PerfExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_PERFUTIL, x, s, __VA_ARGS__)
8#define PerfExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_PERFUTIL, x, s, __VA_ARGS__)
9#define PerfExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_PERFUTIL, x, s, __VA_ARGS__)
10#define PerfExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_PERFUTIL, x, s, __VA_ARGS__)
11#define PerfExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_PERFUTIL, x, s, __VA_ARGS__)
12#define PerfExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_PERFUTIL, x, s, __VA_ARGS__)
13#define PerfExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_PERFUTIL, p, x, e, s, __VA_ARGS__)
14#define PerfExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_PERFUTIL, p, x, s, __VA_ARGS__)
15#define PerfExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_PERFUTIL, p, x, e, s, __VA_ARGS__)
16#define PerfExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_PERFUTIL, p, x, s, __VA_ARGS__)
17#define PerfExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_PERFUTIL, e, x, s, __VA_ARGS__)
18#define PerfExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_PERFUTIL, g, x, s, __VA_ARGS__)
19
20static BOOL vfHighPerformanceCounter = TRUE; // assume the system has a high performance counter
21static double vdFrequency = 1;
22
23
24/********************************************************************
25 PerfInitialize - initializes internal static variables
26
27********************************************************************/
28extern "C" void DAPI PerfInitialize(
29 )
30{
31 LARGE_INTEGER liFrequency = { };
32
33 //
34 // check for high perf counter
35 //
36 if (!::QueryPerformanceFrequency(&liFrequency))
37 {
38 vfHighPerformanceCounter = FALSE;
39 vdFrequency = 1000; // ticks are measured in milliseconds
40 }
41 else
42 vdFrequency = static_cast<double>(liFrequency.QuadPart);
43}
44
45
46/********************************************************************
47 PerfClickTime - resets the clicker, or returns elapsed time since last call
48
49 NOTE: if pliElapsed is NULL, resets the elapsed time
50 if pliElapsed is not NULL, returns perf number since last call to PerfClickTime()
51********************************************************************/
52extern "C" void DAPI PerfClickTime(
53 __out_opt LARGE_INTEGER* pliElapsed
54 )
55{
56 static LARGE_INTEGER liStart = { };
57 LARGE_INTEGER* pli = pliElapsed;
58
59 if (!pli) // if elapsed time time was not requested, reset the start time
60 pli = &liStart;
61
62 if (vfHighPerformanceCounter)
63 ::QueryPerformanceCounter(pli);
64 else
65 pli->QuadPart = ::GetTickCount();
66
67 if (pliElapsed)
68 pliElapsed->QuadPart -= liStart.QuadPart;
69}
70
71
72/********************************************************************
73 PerfConvertToSeconds - converts perf number to seconds
74
75********************************************************************/
76extern "C" double DAPI PerfConvertToSeconds(
77 __in const LARGE_INTEGER* pli
78 )
79{
80 Assert(0 < vdFrequency);
81 return pli->QuadPart / vdFrequency;
82}
diff --git a/src/libs/dutil/WixToolset.DUtil/polcutil.cpp b/src/libs/dutil/WixToolset.DUtil/polcutil.cpp
new file mode 100644
index 00000000..1fdfa18c
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/polcutil.cpp
@@ -0,0 +1,126 @@
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// Exit macros
7#define PolcExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_POLCUTIL, x, s, __VA_ARGS__)
8#define PolcExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_POLCUTIL, x, s, __VA_ARGS__)
9#define PolcExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_POLCUTIL, x, s, __VA_ARGS__)
10#define PolcExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_POLCUTIL, x, s, __VA_ARGS__)
11#define PolcExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_POLCUTIL, x, s, __VA_ARGS__)
12#define PolcExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_POLCUTIL, x, s, __VA_ARGS__)
13#define PolcExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_POLCUTIL, p, x, e, s, __VA_ARGS__)
14#define PolcExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_POLCUTIL, p, x, s, __VA_ARGS__)
15#define PolcExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_POLCUTIL, p, x, e, s, __VA_ARGS__)
16#define PolcExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_POLCUTIL, p, x, s, __VA_ARGS__)
17#define PolcExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_POLCUTIL, e, x, s, __VA_ARGS__)
18#define PolcExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_POLCUTIL, g, x, s, __VA_ARGS__)
19
20const LPCWSTR REGISTRY_POLICIES_KEY = L"SOFTWARE\\Policies\\";
21
22static HRESULT OpenPolicyKey(
23 __in_z LPCWSTR wzPolicyPath,
24 __out HKEY* phk
25 );
26
27
28extern "C" HRESULT DAPI PolcReadNumber(
29 __in_z LPCWSTR wzPolicyPath,
30 __in_z LPCWSTR wzPolicyName,
31 __in DWORD dwDefault,
32 __out DWORD* pdw
33 )
34{
35 HRESULT hr = S_OK;
36 HKEY hk = NULL;
37
38 hr = OpenPolicyKey(wzPolicyPath, &hk);
39 if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr)
40 {
41 ExitFunction1(hr = S_FALSE);
42 }
43 PolcExitOnFailure(hr, "Failed to open policy key: %ls", wzPolicyPath);
44
45 hr = RegReadNumber(hk, wzPolicyName, pdw);
46 if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr)
47 {
48 ExitFunction1(hr = S_FALSE);
49 }
50 PolcExitOnFailure(hr, "Failed to open policy key: %ls, name: %ls", wzPolicyPath, wzPolicyName);
51
52LExit:
53 ReleaseRegKey(hk);
54
55 if (S_FALSE == hr || FAILED(hr))
56 {
57 *pdw = dwDefault;
58 }
59
60 return hr;
61}
62
63extern "C" HRESULT DAPI PolcReadString(
64 __in_z LPCWSTR wzPolicyPath,
65 __in_z LPCWSTR wzPolicyName,
66 __in_z_opt LPCWSTR wzDefault,
67 __deref_out_z LPWSTR* pscz
68 )
69{
70 HRESULT hr = S_OK;
71 HKEY hk = NULL;
72
73 hr = OpenPolicyKey(wzPolicyPath, &hk);
74 if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr)
75 {
76 ExitFunction1(hr = S_FALSE);
77 }
78 PolcExitOnFailure(hr, "Failed to open policy key: %ls", wzPolicyPath);
79
80 hr = RegReadString(hk, wzPolicyName, pscz);
81 if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr)
82 {
83 ExitFunction1(hr = S_FALSE);
84 }
85 PolcExitOnFailure(hr, "Failed to open policy key: %ls, name: %ls", wzPolicyPath, wzPolicyName);
86
87LExit:
88 ReleaseRegKey(hk);
89
90 if (S_FALSE == hr || FAILED(hr))
91 {
92 if (NULL == wzDefault)
93 {
94 ReleaseNullStr(*pscz);
95 }
96 else
97 {
98 hr = StrAllocString(pscz, wzDefault, 0);
99 }
100 }
101
102 return hr;
103}
104
105
106// internal functions
107
108static HRESULT OpenPolicyKey(
109 __in_z LPCWSTR wzPolicyPath,
110 __out HKEY* phk
111 )
112{
113 HRESULT hr = S_OK;
114 LPWSTR sczPath = NULL;
115
116 hr = PathConcat(REGISTRY_POLICIES_KEY, wzPolicyPath, &sczPath);
117 PolcExitOnFailure(hr, "Failed to combine logging path with root path.");
118
119 hr = RegOpen(HKEY_LOCAL_MACHINE, sczPath, KEY_READ, phk);
120 PolcExitOnFailure(hr, "Failed to open policy registry key.");
121
122LExit:
123 ReleaseStr(sczPath);
124
125 return hr;
126}
diff --git a/src/libs/dutil/WixToolset.DUtil/precomp.h b/src/libs/dutil/WixToolset.DUtil/precomp.h
new file mode 100644
index 00000000..f8f3b944
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/precomp.h
@@ -0,0 +1,98 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#ifndef _WIN32_WINNT
6#define _WIN32_WINNT 0x0500
7#endif
8
9#ifndef _WIN32_MSI
10#define _WIN32_MSI 200
11#endif
12
13#define JET_VERSION 0x0501
14
15#include <WinSock2.h>
16#include <windows.h>
17#include <windowsx.h>
18#include <intsafe.h>
19#include <strsafe.h>
20#include <wininet.h>
21#include <msi.h>
22#include <msiquery.h>
23#include <psapi.h>
24#include <shlobj.h>
25#include <shlwapi.h>
26#include <gdiplus.h>
27#include <Tlhelp32.h>
28#include <lm.h>
29#include <Iads.h>
30#include <activeds.h>
31#include <richedit.h>
32#include <stddef.h>
33#include <esent.h>
34#include <ahadmin.h>
35#include <SRRestorePtAPI.h>
36#include <userenv.h>
37#include <WinIoCtl.h>
38#include <wtsapi32.h>
39#include <wuapi.h>
40#include <commctrl.h>
41#include <dbt.h>
42#include <ShellScalingApi.h>
43
44#include "dutilsources.h"
45#include "dutil.h"
46#include "verutil.h"
47#include "aclutil.h"
48#include "atomutil.h"
49#include "buffutil.h"
50#include "butil.h"
51#include "cabcutil.h"
52#include "cabutil.h"
53#include "conutil.h"
54#include "cryputil.h"
55#include "eseutil.h"
56#include "dirutil.h"
57#include "dlutil.h"
58#include "dpiutil.h"
59#include "fileutil.h"
60#include "guidutil.h"
61#include "gdiputil.h"
62#include "dictutil.h"
63#include "deputil.h" // NOTE: This must come after dictutil.h since it uses it.
64#include "inetutil.h"
65#include "iniutil.h"
66#include "jsonutil.h"
67#include "locutil.h"
68#include "logutil.h"
69#include "memutil.h" // NOTE: almost everying is inlined so there is a small .cpp file
70//#include "metautil.h" - see metautil.cpp why this *must* be commented out
71#include "monutil.h"
72#include "osutil.h"
73#include "pathutil.h"
74#include "perfutil.h"
75#include "polcutil.h"
76#include "procutil.h"
77#include "regutil.h"
78#include "resrutil.h"
79#include "reswutil.h"
80#include "rmutil.h"
81#include "rssutil.h"
82#include "apuputil.h" // NOTE: this must come after atomutil.h and rssutil.h since it uses them.
83#include "shelutil.h"
84//#include "sqlutil.h" - see sqlutil.cpp why this *must* be commented out
85#include "srputil.h"
86#include "strutil.h"
87#include "timeutil.h"
88#include "timeutil.h"
89#include "thmutil.h"
90#include "uncutil.h"
91#include "uriutil.h"
92#include "userutil.h"
93#include "wiutil.h"
94#include "wuautil.h"
95#include <comutil.h> // This header is needed for msxml2.h to compile correctly
96#include <msxml2.h> // This file is needed to include xmlutil.h
97#include "xmlutil.h"
98
diff --git a/src/libs/dutil/WixToolset.DUtil/proc2utl.cpp b/src/libs/dutil/WixToolset.DUtil/proc2utl.cpp
new file mode 100644
index 00000000..a59d2ffc
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/proc2utl.cpp
@@ -0,0 +1,83 @@
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// Exit macros
7#define ProcExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__)
8#define ProcExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__)
9#define ProcExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__)
10#define ProcExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__)
11#define ProcExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__)
12#define ProcExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__)
13#define ProcExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_PROCUTIL, p, x, e, s, __VA_ARGS__)
14#define ProcExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, p, x, s, __VA_ARGS__)
15#define ProcExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_PROCUTIL, p, x, e, s, __VA_ARGS__)
16#define ProcExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, p, x, s, __VA_ARGS__)
17#define ProcExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_PROCUTIL, e, x, s, __VA_ARGS__)
18#define ProcExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_PROCUTIL, g, x, s, __VA_ARGS__)
19
20/********************************************************************
21 ProcFindAllIdsFromExeName() - returns an array of process ids that are running specified executable.
22
23*******************************************************************/
24extern "C" HRESULT DAPI ProcFindAllIdsFromExeName(
25 __in_z LPCWSTR wzExeName,
26 __out DWORD** ppdwProcessIds,
27 __out DWORD* pcProcessIds
28 )
29{
30 HRESULT hr = S_OK;
31 DWORD er = ERROR_SUCCESS;
32 HANDLE hSnap = INVALID_HANDLE_VALUE;
33 BOOL fContinue = FALSE;
34 PROCESSENTRY32W peData = { sizeof(peData) };
35
36 hSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
37 if (INVALID_HANDLE_VALUE == hSnap)
38 {
39 ProcExitWithLastError(hr, "Failed to create snapshot of processes on system");
40 }
41
42 fContinue = ::Process32FirstW(hSnap, &peData);
43
44 while (fContinue)
45 {
46 if (0 == lstrcmpiW((LPCWSTR)&(peData.szExeFile), wzExeName))
47 {
48 if (!*ppdwProcessIds)
49 {
50 *ppdwProcessIds = static_cast<DWORD*>(MemAlloc(sizeof(DWORD), TRUE));
51 ProcExitOnNull(ppdwProcessIds, hr, E_OUTOFMEMORY, "Failed to allocate array for returned process IDs.");
52 }
53 else
54 {
55 DWORD* pdwReAllocReturnedPids = NULL;
56 pdwReAllocReturnedPids = static_cast<DWORD*>(MemReAlloc(*ppdwProcessIds, sizeof(DWORD) * ((*pcProcessIds) + 1), TRUE));
57 ProcExitOnNull(pdwReAllocReturnedPids, hr, E_OUTOFMEMORY, "Failed to re-allocate array for returned process IDs.");
58
59 *ppdwProcessIds = pdwReAllocReturnedPids;
60 }
61
62 (*ppdwProcessIds)[*pcProcessIds] = peData.th32ProcessID;
63 ++(*pcProcessIds);
64 }
65
66 fContinue = ::Process32NextW(hSnap, &peData);
67 }
68
69 er = ::GetLastError();
70 if (ERROR_NO_MORE_FILES == er)
71 {
72 hr = S_OK;
73 }
74 else
75 {
76 hr = HRESULT_FROM_WIN32(er);
77 }
78
79LExit:
80 ReleaseFile(hSnap);
81
82 return hr;
83}
diff --git a/src/libs/dutil/WixToolset.DUtil/proc3utl.cpp b/src/libs/dutil/WixToolset.DUtil/proc3utl.cpp
new file mode 100644
index 00000000..6d3cbc67
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/proc3utl.cpp
@@ -0,0 +1,129 @@
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// Exit macros
7#define ProcExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__)
8#define ProcExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__)
9#define ProcExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__)
10#define ProcExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__)
11#define ProcExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__)
12#define ProcExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__)
13#define ProcExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_PROCUTIL, p, x, e, s, __VA_ARGS__)
14#define ProcExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, p, x, s, __VA_ARGS__)
15#define ProcExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_PROCUTIL, p, x, e, s, __VA_ARGS__)
16#define ProcExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, p, x, s, __VA_ARGS__)
17#define ProcExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_PROCUTIL, e, x, s, __VA_ARGS__)
18#define ProcExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_PROCUTIL, g, x, s, __VA_ARGS__)
19
20static HRESULT GetActiveSessionUserToken(
21 __out HANDLE *phToken
22 );
23
24
25/********************************************************************
26 ProcExecuteAsInteractiveUser() - runs process as currently logged in
27 user.
28*******************************************************************/
29extern "C" HRESULT DAPI ProcExecuteAsInteractiveUser(
30 __in_z LPCWSTR wzExecutablePath,
31 __in_z LPCWSTR wzCommandLine,
32 __out HANDLE *phProcess
33 )
34{
35 HRESULT hr = S_OK;
36 HANDLE hToken = NULL;
37 LPVOID pEnvironment = NULL;
38 LPWSTR sczFullCommandLine = NULL;
39 STARTUPINFOW si = { };
40 PROCESS_INFORMATION pi = { };
41
42 hr = GetActiveSessionUserToken(&hToken);
43 ProcExitOnFailure(hr, "Failed to get active session user token.");
44
45 if (!::CreateEnvironmentBlock(&pEnvironment, hToken, FALSE))
46 {
47 ProcExitWithLastError(hr, "Failed to create environment block for UI process.");
48 }
49
50 hr = StrAllocFormatted(&sczFullCommandLine, L"\"%ls\" %ls", wzExecutablePath, wzCommandLine);
51 ProcExitOnFailure(hr, "Failed to allocate full command-line.");
52
53 si.cb = sizeof(si);
54 if (!::CreateProcessAsUserW(hToken, wzExecutablePath, sczFullCommandLine, NULL, NULL, FALSE, CREATE_UNICODE_ENVIRONMENT, pEnvironment, NULL, &si, &pi))
55 {
56 ProcExitWithLastError(hr, "Failed to create UI process: %ls", sczFullCommandLine);
57 }
58
59 *phProcess = pi.hProcess;
60 pi.hProcess = NULL;
61
62LExit:
63 ReleaseHandle(pi.hThread);
64 ReleaseHandle(pi.hProcess);
65 ReleaseStr(sczFullCommandLine);
66
67 if (pEnvironment)
68 {
69 ::DestroyEnvironmentBlock(pEnvironment);
70 }
71
72 ReleaseHandle(hToken);
73
74 return hr;
75}
76
77
78static HRESULT GetActiveSessionUserToken(
79 __out HANDLE *phToken
80 )
81{
82 HRESULT hr = S_OK;
83 PWTS_SESSION_INFO pSessionInfo = NULL;
84 DWORD cSessions = 0;
85 DWORD dwSessionId = 0;
86 BOOL fSessionFound = FALSE;
87 HANDLE hToken = NULL;
88
89 // Loop through the sessions looking for the active one.
90 if (!::WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pSessionInfo, &cSessions))
91 {
92 ProcExitWithLastError(hr, "Failed to enumerate sessions.");
93 }
94
95 for (DWORD i = 0; i < cSessions; ++i)
96 {
97 if (WTSActive == pSessionInfo[i].State)
98 {
99 dwSessionId = pSessionInfo[i].SessionId;
100 fSessionFound = TRUE;
101
102 break;
103 }
104 }
105
106 if (!fSessionFound)
107 {
108 ExitFunction1(hr = E_NOTFOUND);
109 }
110
111 // Get the user token from the active session.
112 if (!::WTSQueryUserToken(dwSessionId, &hToken))
113 {
114 ProcExitWithLastError(hr, "Failed to get active session user token.");
115 }
116
117 *phToken = hToken;
118 hToken = NULL;
119
120LExit:
121 ReleaseHandle(hToken);
122
123 if (pSessionInfo)
124 {
125 ::WTSFreeMemory(pSessionInfo);
126 }
127
128 return hr;
129}
diff --git a/src/libs/dutil/WixToolset.DUtil/procutil.cpp b/src/libs/dutil/WixToolset.DUtil/procutil.cpp
new file mode 100644
index 00000000..6bfe5017
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/procutil.cpp
@@ -0,0 +1,522 @@
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// Exit macros
7#define ProcExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__)
8#define ProcExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__)
9#define ProcExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__)
10#define ProcExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__)
11#define ProcExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__)
12#define ProcExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__)
13#define ProcExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_PROCUTIL, p, x, e, s, __VA_ARGS__)
14#define ProcExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, p, x, s, __VA_ARGS__)
15#define ProcExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_PROCUTIL, p, x, e, s, __VA_ARGS__)
16#define ProcExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, p, x, s, __VA_ARGS__)
17#define ProcExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_PROCUTIL, e, x, s, __VA_ARGS__)
18#define ProcExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_PROCUTIL, g, x, s, __VA_ARGS__)
19
20
21// private functions
22static HRESULT CreatePipes(
23 __out HANDLE *phOutRead,
24 __out HANDLE *phOutWrite,
25 __out HANDLE *phErrWrite,
26 __out HANDLE *phInRead,
27 __out HANDLE *phInWrite
28 );
29
30static BOOL CALLBACK CloseWindowEnumCallback(
31 __in HWND hWnd,
32 __in LPARAM lParam
33 );
34
35
36extern "C" HRESULT DAPI ProcElevated(
37 __in HANDLE hProcess,
38 __out BOOL* pfElevated
39 )
40{
41 HRESULT hr = S_OK;
42 HANDLE hToken = NULL;
43 TOKEN_ELEVATION tokenElevated = { };
44 DWORD cbToken = 0;
45
46 if (!::OpenProcessToken(hProcess, TOKEN_QUERY, &hToken))
47 {
48 ProcExitWithLastError(hr, "Failed to open process token.");
49 }
50
51 if (::GetTokenInformation(hToken, TokenElevation, &tokenElevated, sizeof(TOKEN_ELEVATION), &cbToken))
52 {
53 *pfElevated = (0 != tokenElevated.TokenIsElevated);
54 }
55 else
56 {
57 DWORD er = ::GetLastError();
58 hr = HRESULT_FROM_WIN32(er);
59
60 // If it's invalid argument, this means the OS doesn't support TokenElevation, so we're not elevated.
61 if (E_INVALIDARG == hr)
62 {
63 *pfElevated = FALSE;
64 hr = S_OK;
65 }
66 else
67 {
68 ProcExitOnRootFailure(hr, "Failed to get elevation token from process.");
69 }
70 }
71
72LExit:
73 ReleaseHandle(hToken);
74
75 return hr;
76}
77
78extern "C" HRESULT DAPI ProcWow64(
79 __in HANDLE hProcess,
80 __out BOOL* pfWow64
81 )
82{
83 HRESULT hr = S_OK;
84 BOOL fIsWow64 = FALSE;
85
86 typedef BOOL(WINAPI* LPFN_ISWOW64PROCESS2)(HANDLE, USHORT *, USHORT *);
87 LPFN_ISWOW64PROCESS2 pfnIsWow64Process2 = (LPFN_ISWOW64PROCESS2)::GetProcAddress(::GetModuleHandleW(L"kernel32"), "IsWow64Process2");
88
89 if (pfnIsWow64Process2)
90 {
91 USHORT pProcessMachine = IMAGE_FILE_MACHINE_UNKNOWN;
92 if (!pfnIsWow64Process2(hProcess, &pProcessMachine, nullptr))
93 {
94 ProcExitWithLastError(hr, "Failed to check WOW64 process - IsWow64Process2.");
95 }
96
97 if (pProcessMachine != IMAGE_FILE_MACHINE_UNKNOWN)
98 {
99 fIsWow64 = TRUE;
100 }
101 }
102 else
103 {
104 typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS)(HANDLE, PBOOL);
105 LPFN_ISWOW64PROCESS pfnIsWow64Process = (LPFN_ISWOW64PROCESS)::GetProcAddress(::GetModuleHandleW(L"kernel32"), "IsWow64Process");
106
107 if (pfnIsWow64Process)
108 {
109 if (!pfnIsWow64Process(hProcess, &fIsWow64))
110 {
111 ProcExitWithLastError(hr, "Failed to check WOW64 process - IsWow64Process.");
112 }
113 }
114 }
115
116 *pfWow64 = fIsWow64;
117
118LExit:
119 return hr;
120}
121
122extern "C" HRESULT DAPI ProcDisableWowFileSystemRedirection(
123 __in PROC_FILESYSTEMREDIRECTION* pfsr
124 )
125{
126 AssertSz(!pfsr->fDisabled, "File system redirection was already disabled.");
127 HRESULT hr = S_OK;
128
129 typedef BOOL (WINAPI *LPFN_Wow64DisableWow64FsRedirection)(PVOID *);
130 LPFN_Wow64DisableWow64FsRedirection pfnWow64DisableWow64FsRedirection = (LPFN_Wow64DisableWow64FsRedirection)::GetProcAddress(::GetModuleHandleW(L"kernel32"), "Wow64DisableWow64FsRedirection");
131
132 if (!pfnWow64DisableWow64FsRedirection)
133 {
134 ExitFunction1(hr = E_NOTIMPL);
135 }
136
137 if (!pfnWow64DisableWow64FsRedirection(&pfsr->pvRevertState))
138 {
139 ProcExitWithLastError(hr, "Failed to disable file system redirection.");
140 }
141
142 pfsr->fDisabled = TRUE;
143
144LExit:
145 return hr;
146}
147
148extern "C" HRESULT DAPI ProcRevertWowFileSystemRedirection(
149 __in PROC_FILESYSTEMREDIRECTION* pfsr
150 )
151{
152 HRESULT hr = S_OK;
153
154 if (pfsr->fDisabled)
155 {
156 typedef BOOL (WINAPI *LPFN_Wow64RevertWow64FsRedirection)(PVOID);
157 LPFN_Wow64RevertWow64FsRedirection pfnWow64RevertWow64FsRedirection = (LPFN_Wow64RevertWow64FsRedirection)::GetProcAddress(::GetModuleHandleW(L"kernel32"), "Wow64RevertWow64FsRedirection");
158
159 if (!pfnWow64RevertWow64FsRedirection(pfsr->pvRevertState))
160 {
161 ProcExitWithLastError(hr, "Failed to revert file system redirection.");
162 }
163
164 pfsr->fDisabled = FALSE;
165 pfsr->pvRevertState = NULL;
166 }
167
168LExit:
169 return hr;
170}
171
172
173extern "C" HRESULT DAPI ProcExec(
174 __in_z LPCWSTR wzExecutablePath,
175 __in_z_opt LPCWSTR wzCommandLine,
176 __in int nCmdShow,
177 __out HANDLE *phProcess
178 )
179{
180 HRESULT hr = S_OK;
181 LPWSTR sczFullCommandLine = NULL;
182 STARTUPINFOW si = { };
183 PROCESS_INFORMATION pi = { };
184
185 hr = StrAllocFormatted(&sczFullCommandLine, L"\"%ls\" %ls", wzExecutablePath, wzCommandLine ? wzCommandLine : L"");
186 ProcExitOnFailure(hr, "Failed to allocate full command-line.");
187
188 si.cb = sizeof(si);
189 si.wShowWindow = static_cast<WORD>(nCmdShow);
190 if (!::CreateProcessW(wzExecutablePath, sczFullCommandLine, NULL, NULL, FALSE, 0, 0, NULL, &si, &pi))
191 {
192 ProcExitWithLastError(hr, "Failed to create process: %ls", sczFullCommandLine);
193 }
194
195 *phProcess = pi.hProcess;
196 pi.hProcess = NULL;
197
198LExit:
199 ReleaseHandle(pi.hThread);
200 ReleaseHandle(pi.hProcess);
201 ReleaseStr(sczFullCommandLine);
202
203 return hr;
204}
205
206
207/********************************************************************
208 ProcExecute() - executes a command-line.
209
210*******************************************************************/
211extern "C" HRESULT DAPI ProcExecute(
212 __in_z LPWSTR wzCommand,
213 __out HANDLE *phProcess,
214 __out_opt HANDLE *phChildStdIn,
215 __out_opt HANDLE *phChildStdOutErr
216 )
217{
218 HRESULT hr = S_OK;
219
220 PROCESS_INFORMATION pi = { };
221 STARTUPINFOW si = { };
222
223 HANDLE hOutRead = INVALID_HANDLE_VALUE;
224 HANDLE hOutWrite = INVALID_HANDLE_VALUE;
225 HANDLE hErrWrite = INVALID_HANDLE_VALUE;
226 HANDLE hInRead = INVALID_HANDLE_VALUE;
227 HANDLE hInWrite = INVALID_HANDLE_VALUE;
228
229 // Create redirect pipes.
230 hr = CreatePipes(&hOutRead, &hOutWrite, &hErrWrite, &hInRead, &hInWrite);
231 ProcExitOnFailure(hr, "failed to create output pipes");
232
233 // Set up startup structure.
234 si.cb = sizeof(STARTUPINFOW);
235 si.dwFlags = STARTF_USESTDHANDLES;
236 si.hStdInput = hInRead;
237 si.hStdOutput = hOutWrite;
238 si.hStdError = hErrWrite;
239
240#pragma prefast(push)
241#pragma prefast(disable:25028)
242 if (::CreateProcessW(NULL,
243 wzCommand, // command line
244 NULL, // security info
245 NULL, // thread info
246 TRUE, // inherit handles
247 ::GetPriorityClass(::GetCurrentProcess()) | CREATE_NO_WINDOW, // creation flags
248 NULL, // environment
249 NULL, // cur dir
250 &si,
251 &pi))
252#pragma prefast(pop)
253 {
254 // Close child process output/input handles so child doesn't hang
255 // while waiting for input from parent process.
256 ::CloseHandle(hOutWrite);
257 hOutWrite = INVALID_HANDLE_VALUE;
258
259 ::CloseHandle(hErrWrite);
260 hErrWrite = INVALID_HANDLE_VALUE;
261
262 ::CloseHandle(hInRead);
263 hInRead = INVALID_HANDLE_VALUE;
264 }
265 else
266 {
267 ProcExitWithLastError(hr, "Process failed to execute.");
268 }
269
270 *phProcess = pi.hProcess;
271 pi.hProcess = 0;
272
273 if (phChildStdIn)
274 {
275 *phChildStdIn = hInWrite;
276 hInWrite = INVALID_HANDLE_VALUE;
277 }
278
279 if (phChildStdOutErr)
280 {
281 *phChildStdOutErr = hOutRead;
282 hOutRead = INVALID_HANDLE_VALUE;
283 }
284
285LExit:
286 if (pi.hThread)
287 {
288 ::CloseHandle(pi.hThread);
289 }
290
291 if (pi.hProcess)
292 {
293 ::CloseHandle(pi.hProcess);
294 }
295
296 ReleaseFileHandle(hOutRead);
297 ReleaseFileHandle(hOutWrite);
298 ReleaseFileHandle(hErrWrite);
299 ReleaseFileHandle(hInRead);
300 ReleaseFileHandle(hInWrite);
301
302 return hr;
303}
304
305
306/********************************************************************
307 ProcWaitForCompletion() - waits for process to complete and gets return code.
308
309*******************************************************************/
310extern "C" HRESULT DAPI ProcWaitForCompletion(
311 __in HANDLE hProcess,
312 __in DWORD dwTimeout,
313 __out DWORD *pReturnCode
314 )
315{
316 HRESULT hr = S_OK;
317 DWORD er = ERROR_SUCCESS;
318
319 // Wait for everything to finish
320 er = ::WaitForSingleObject(hProcess, dwTimeout);
321 if (WAIT_FAILED == er)
322 {
323 ProcExitWithLastError(hr, "Failed to wait for process to complete.");
324 }
325 else if (WAIT_TIMEOUT == er)
326 {
327 ExitFunction1(hr = HRESULT_FROM_WIN32(er));
328 }
329
330 if (!::GetExitCodeProcess(hProcess, &er))
331 {
332 ProcExitWithLastError(hr, "Failed to get process return code.");
333 }
334
335 *pReturnCode = er;
336
337LExit:
338 return hr;
339}
340
341/********************************************************************
342 ProcWaitForIds() - waits for multiple processes to complete.
343
344*******************************************************************/
345extern "C" HRESULT DAPI ProcWaitForIds(
346 __in_ecount(cProcessIds) const DWORD rgdwProcessIds[],
347 __in DWORD cProcessIds,
348 __in DWORD dwMilliseconds
349 )
350{
351 HRESULT hr = S_OK;
352 DWORD er = ERROR_SUCCESS;
353 HANDLE hProcess = NULL;
354 HANDLE * rghProcesses = NULL;
355 DWORD cProcesses = 0;
356
357 rghProcesses = static_cast<HANDLE*>(MemAlloc(sizeof(DWORD) * cProcessIds, TRUE));
358 ProcExitOnNull(rgdwProcessIds, hr, E_OUTOFMEMORY, "Failed to allocate array for process ID Handles.");
359
360 for (DWORD i = 0; i < cProcessIds; ++i)
361 {
362 hProcess = ::OpenProcess(SYNCHRONIZE, FALSE, rgdwProcessIds[i]);
363 if (hProcess != NULL)
364 {
365 rghProcesses[cProcesses++] = hProcess;
366 }
367 }
368
369 er = ::WaitForMultipleObjects(cProcesses, rghProcesses, TRUE, dwMilliseconds);
370 if (WAIT_FAILED == er)
371 {
372 ProcExitWithLastError(hr, "Failed to wait for process to complete.");
373 }
374 else if (WAIT_TIMEOUT == er)
375 {
376 ProcExitOnWin32Error(er, hr, "Timed out while waiting for process to complete.");
377 }
378
379LExit:
380 if (rghProcesses)
381 {
382 for (DWORD i = 0; i < cProcesses; ++i)
383 {
384 if (NULL != rghProcesses[i])
385 {
386 ::CloseHandle(rghProcesses[i]);
387 }
388 }
389
390 MemFree(rghProcesses);
391 }
392
393 return hr;
394}
395
396/********************************************************************
397 ProcCloseIds() - sends WM_CLOSE messages to all process ids.
398
399*******************************************************************/
400extern "C" HRESULT DAPI ProcCloseIds(
401 __in_ecount(cProcessIds) const DWORD* pdwProcessIds,
402 __in DWORD cProcessIds
403 )
404{
405 HRESULT hr = S_OK;
406
407 for (DWORD i = 0; i < cProcessIds; ++i)
408 {
409 if (!::EnumWindows(&CloseWindowEnumCallback, pdwProcessIds[i]))
410 {
411 ProcExitWithLastError(hr, "Failed to enumerate windows.");
412 }
413 }
414
415LExit:
416 return hr;
417}
418
419
420static HRESULT CreatePipes(
421 __out HANDLE *phOutRead,
422 __out HANDLE *phOutWrite,
423 __out HANDLE *phErrWrite,
424 __out HANDLE *phInRead,
425 __out HANDLE *phInWrite
426 )
427{
428 HRESULT hr = S_OK;
429 SECURITY_ATTRIBUTES sa;
430 HANDLE hOutTemp = INVALID_HANDLE_VALUE;
431 HANDLE hInTemp = INVALID_HANDLE_VALUE;
432
433 HANDLE hOutRead = INVALID_HANDLE_VALUE;
434 HANDLE hOutWrite = INVALID_HANDLE_VALUE;
435 HANDLE hErrWrite = INVALID_HANDLE_VALUE;
436 HANDLE hInRead = INVALID_HANDLE_VALUE;
437 HANDLE hInWrite = INVALID_HANDLE_VALUE;
438
439 // Fill out security structure so we can inherit handles
440 ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));
441 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
442 sa.bInheritHandle = TRUE;
443 sa.lpSecurityDescriptor = NULL;
444
445 // Create pipes
446 if (!::CreatePipe(&hOutTemp, &hOutWrite, &sa, 0))
447 {
448 ProcExitWithLastError(hr, "failed to create output pipe");
449 }
450
451 if (!::CreatePipe(&hInRead, &hInTemp, &sa, 0))
452 {
453 ProcExitWithLastError(hr, "failed to create input pipe");
454 }
455
456 // Duplicate output pipe so standard error and standard output write to the same pipe.
457 if (!::DuplicateHandle(::GetCurrentProcess(), hOutWrite, ::GetCurrentProcess(), &hErrWrite, 0, TRUE, DUPLICATE_SAME_ACCESS))
458 {
459 ProcExitWithLastError(hr, "failed to duplicate write handle");
460 }
461
462 // We need to create new "output read" and "input write" handles that are non inheritable. Otherwise CreateProcess will creates handles in
463 // the child process that can't be closed.
464 if (!::DuplicateHandle(::GetCurrentProcess(), hOutTemp, ::GetCurrentProcess(), &hOutRead, 0, FALSE, DUPLICATE_SAME_ACCESS))
465 {
466 ProcExitWithLastError(hr, "failed to duplicate output pipe");
467 }
468
469 if (!::DuplicateHandle(::GetCurrentProcess(), hInTemp, ::GetCurrentProcess(), &hInWrite, 0, FALSE, DUPLICATE_SAME_ACCESS))
470 {
471 ProcExitWithLastError(hr, "failed to duplicate input pipe");
472 }
473
474 // now that everything has succeeded, assign to the outputs
475 *phOutRead = hOutRead;
476 hOutRead = INVALID_HANDLE_VALUE;
477
478 *phOutWrite = hOutWrite;
479 hOutWrite = INVALID_HANDLE_VALUE;
480
481 *phErrWrite = hErrWrite;
482 hErrWrite = INVALID_HANDLE_VALUE;
483
484 *phInRead = hInRead;
485 hInRead = INVALID_HANDLE_VALUE;
486
487 *phInWrite = hInWrite;
488 hInWrite = INVALID_HANDLE_VALUE;
489
490LExit:
491 ReleaseFileHandle(hOutRead);
492 ReleaseFileHandle(hOutWrite);
493 ReleaseFileHandle(hErrWrite);
494 ReleaseFileHandle(hInRead);
495 ReleaseFileHandle(hInWrite);
496 ReleaseFileHandle(hOutTemp);
497 ReleaseFileHandle(hInTemp);
498
499 return hr;
500}
501
502
503/********************************************************************
504 CloseWindowEnumCallback() - outputs trace and log info
505
506*******************************************************************/
507static BOOL CALLBACK CloseWindowEnumCallback(
508 __in HWND hWnd,
509 __in LPARAM lParam
510 )
511{
512 DWORD dwPid = static_cast<DWORD>(lParam);
513 DWORD dwProcessId = 0;
514
515 ::GetWindowThreadProcessId(hWnd, &dwProcessId);
516 if (dwPid == dwProcessId)
517 {
518 ::SendMessageW(hWnd, WM_CLOSE, 0, 0);
519 }
520
521 return TRUE;
522}
diff --git a/src/libs/dutil/WixToolset.DUtil/regutil.cpp b/src/libs/dutil/WixToolset.DUtil/regutil.cpp
new file mode 100644
index 00000000..cb617932
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/regutil.cpp
@@ -0,0 +1,1035 @@
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// Exit macros
7#define RegExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_REGUTIL, x, s, __VA_ARGS__)
8#define RegExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_REGUTIL, x, s, __VA_ARGS__)
9#define RegExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_REGUTIL, x, s, __VA_ARGS__)
10#define RegExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_REGUTIL, x, s, __VA_ARGS__)
11#define RegExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_REGUTIL, x, s, __VA_ARGS__)
12#define RegExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_REGUTIL, x, s, __VA_ARGS__)
13#define RegExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_REGUTIL, p, x, e, s, __VA_ARGS__)
14#define RegExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_REGUTIL, p, x, s, __VA_ARGS__)
15#define RegExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_REGUTIL, p, x, e, s, __VA_ARGS__)
16#define RegExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_REGUTIL, p, x, s, __VA_ARGS__)
17#define RegExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_REGUTIL, e, x, s, __VA_ARGS__)
18#define RegExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_REGUTIL, g, x, s, __VA_ARGS__)
19
20static PFN_REGCREATEKEYEXW vpfnRegCreateKeyExW = ::RegCreateKeyExW;
21static PFN_REGOPENKEYEXW vpfnRegOpenKeyExW = ::RegOpenKeyExW;
22static PFN_REGDELETEKEYEXW vpfnRegDeleteKeyExW = NULL;
23static PFN_REGDELETEKEYEXW vpfnRegDeleteKeyExWFromLibrary = NULL;
24static PFN_REGDELETEKEYW vpfnRegDeleteKeyW = ::RegDeleteKeyW;
25static PFN_REGENUMKEYEXW vpfnRegEnumKeyExW = ::RegEnumKeyExW;
26static PFN_REGENUMVALUEW vpfnRegEnumValueW = ::RegEnumValueW;
27static PFN_REGQUERYINFOKEYW vpfnRegQueryInfoKeyW = ::RegQueryInfoKeyW;
28static PFN_REGQUERYVALUEEXW vpfnRegQueryValueExW = ::RegQueryValueExW;
29static PFN_REGSETVALUEEXW vpfnRegSetValueExW = ::RegSetValueExW;
30static PFN_REGDELETEVALUEW vpfnRegDeleteValueW = ::RegDeleteValueW;
31
32static HMODULE vhAdvApi32Dll = NULL;
33static BOOL vfRegInitialized = FALSE;
34
35static HRESULT WriteStringToRegistry(
36 __in HKEY hk,
37 __in_z_opt LPCWSTR wzName,
38 __in_z_opt LPCWSTR wzValue,
39 __in DWORD dwType
40);
41
42/********************************************************************
43 RegInitialize - initializes regutil
44
45*********************************************************************/
46extern "C" HRESULT DAPI RegInitialize(
47 )
48{
49 HRESULT hr = S_OK;
50
51 hr = LoadSystemLibrary(L"AdvApi32.dll", &vhAdvApi32Dll);
52 RegExitOnFailure(hr, "Failed to load AdvApi32.dll");
53
54 // ignore failures - if this doesn't exist, we'll fall back to RegDeleteKeyW
55 vpfnRegDeleteKeyExWFromLibrary = reinterpret_cast<PFN_REGDELETEKEYEXW>(::GetProcAddress(vhAdvApi32Dll, "RegDeleteKeyExW"));
56
57 if (NULL == vpfnRegDeleteKeyExW)
58 {
59 vpfnRegDeleteKeyExW = vpfnRegDeleteKeyExWFromLibrary;
60 }
61
62 vfRegInitialized = TRUE;
63
64LExit:
65 return hr;
66}
67
68
69/********************************************************************
70 RegUninitialize - uninitializes regutil
71
72*********************************************************************/
73extern "C" void DAPI RegUninitialize(
74 )
75{
76 if (vhAdvApi32Dll)
77 {
78 ::FreeLibrary(vhAdvApi32Dll);
79 vhAdvApi32Dll = NULL;
80 vpfnRegDeleteKeyExWFromLibrary = NULL;
81 vpfnRegDeleteKeyExW = NULL;
82 }
83
84 vfRegInitialized = FALSE;
85}
86
87
88/********************************************************************
89 RegFunctionOverride - overrides the registry functions. Typically used
90 for unit testing.
91
92*********************************************************************/
93extern "C" void DAPI RegFunctionOverride(
94 __in_opt PFN_REGCREATEKEYEXW pfnRegCreateKeyExW,
95 __in_opt PFN_REGOPENKEYEXW pfnRegOpenKeyExW,
96 __in_opt PFN_REGDELETEKEYEXW pfnRegDeleteKeyExW,
97 __in_opt PFN_REGENUMKEYEXW pfnRegEnumKeyExW,
98 __in_opt PFN_REGENUMVALUEW pfnRegEnumValueW,
99 __in_opt PFN_REGQUERYINFOKEYW pfnRegQueryInfoKeyW,
100 __in_opt PFN_REGQUERYVALUEEXW pfnRegQueryValueExW,
101 __in_opt PFN_REGSETVALUEEXW pfnRegSetValueExW,
102 __in_opt PFN_REGDELETEVALUEW pfnRegDeleteValueW
103 )
104{
105 vpfnRegCreateKeyExW = pfnRegCreateKeyExW ? pfnRegCreateKeyExW : ::RegCreateKeyExW;
106 vpfnRegOpenKeyExW = pfnRegOpenKeyExW ? pfnRegOpenKeyExW : ::RegOpenKeyExW;
107 vpfnRegDeleteKeyExW = pfnRegDeleteKeyExW ? pfnRegDeleteKeyExW : vpfnRegDeleteKeyExWFromLibrary;
108 vpfnRegEnumKeyExW = pfnRegEnumKeyExW ? pfnRegEnumKeyExW : ::RegEnumKeyExW;
109 vpfnRegEnumValueW = pfnRegEnumValueW ? pfnRegEnumValueW : ::RegEnumValueW;
110 vpfnRegQueryInfoKeyW = pfnRegQueryInfoKeyW ? pfnRegQueryInfoKeyW : ::RegQueryInfoKeyW;
111 vpfnRegQueryValueExW = pfnRegQueryValueExW ? pfnRegQueryValueExW : ::RegQueryValueExW;
112 vpfnRegSetValueExW = pfnRegSetValueExW ? pfnRegSetValueExW : ::RegSetValueExW;
113 vpfnRegDeleteValueW = pfnRegDeleteValueW ? pfnRegDeleteValueW : ::RegDeleteValueW;
114}
115
116
117/********************************************************************
118 RegCreate - creates a registry key.
119
120*********************************************************************/
121extern "C" HRESULT DAPI RegCreate(
122 __in HKEY hkRoot,
123 __in_z LPCWSTR wzSubKey,
124 __in DWORD dwAccess,
125 __out HKEY* phk
126 )
127{
128 HRESULT hr = S_OK;
129 DWORD er = ERROR_SUCCESS;
130
131 er = vpfnRegCreateKeyExW(hkRoot, wzSubKey, 0, NULL, REG_OPTION_NON_VOLATILE, dwAccess, NULL, phk, NULL);
132 RegExitOnWin32Error(er, hr, "Failed to create registry key.");
133
134LExit:
135 return hr;
136}
137
138
139/********************************************************************
140 RegCreate - creates a registry key with extra options.
141
142*********************************************************************/
143HRESULT DAPI RegCreateEx(
144 __in HKEY hkRoot,
145 __in_z LPCWSTR wzSubKey,
146 __in DWORD dwAccess,
147 __in BOOL fVolatile,
148 __in_opt SECURITY_ATTRIBUTES* pSecurityAttributes,
149 __out HKEY* phk,
150 __out_opt BOOL* pfCreated
151 )
152{
153 HRESULT hr = S_OK;
154 DWORD er = ERROR_SUCCESS;
155 DWORD dwDisposition;
156
157 er = vpfnRegCreateKeyExW(hkRoot, wzSubKey, 0, NULL, fVolatile ? REG_OPTION_VOLATILE : REG_OPTION_NON_VOLATILE, dwAccess, pSecurityAttributes, phk, &dwDisposition);
158 RegExitOnWin32Error(er, hr, "Failed to create registry key.");
159
160 if (pfCreated)
161 {
162 *pfCreated = (REG_CREATED_NEW_KEY == dwDisposition);
163 }
164
165LExit:
166 return hr;
167}
168
169
170/********************************************************************
171 RegOpen - opens a registry key.
172
173*********************************************************************/
174extern "C" HRESULT DAPI RegOpen(
175 __in HKEY hkRoot,
176 __in_z LPCWSTR wzSubKey,
177 __in DWORD dwAccess,
178 __out HKEY* phk
179 )
180{
181 HRESULT hr = S_OK;
182 DWORD er = ERROR_SUCCESS;
183
184 er = vpfnRegOpenKeyExW(hkRoot, wzSubKey, 0, dwAccess, phk);
185 if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er))
186 {
187 ExitFunction1(hr = E_FILENOTFOUND);
188 }
189 RegExitOnWin32Error(er, hr, "Failed to open registry key.");
190
191LExit:
192 return hr;
193}
194
195
196/********************************************************************
197 RegDelete - deletes a registry key (and optionally it's whole tree).
198
199*********************************************************************/
200extern "C" HRESULT DAPI RegDelete(
201 __in HKEY hkRoot,
202 __in_z LPCWSTR wzSubKey,
203 __in REG_KEY_BITNESS kbKeyBitness,
204 __in BOOL fDeleteTree
205 )
206{
207 HRESULT hr = S_OK;
208 DWORD er = ERROR_SUCCESS;
209 LPWSTR pszEnumeratedSubKey = NULL;
210 LPWSTR pszRecursiveSubKey = NULL;
211 HKEY hkKey = NULL;
212 REGSAM samDesired = 0;
213
214 if (!vfRegInitialized && REG_KEY_DEFAULT != kbKeyBitness)
215 {
216 hr = E_INVALIDARG;
217 RegExitOnFailure(hr, "RegInitialize must be called first in order to RegDelete() a key with non-default bit attributes!");
218 }
219
220 switch (kbKeyBitness)
221 {
222 case REG_KEY_32BIT:
223 samDesired = KEY_WOW64_32KEY;
224 break;
225 case REG_KEY_64BIT:
226 samDesired = KEY_WOW64_64KEY;
227 break;
228 case REG_KEY_DEFAULT:
229 // Nothing to do
230 break;
231 }
232
233 if (fDeleteTree)
234 {
235 hr = RegOpen(hkRoot, wzSubKey, KEY_READ | samDesired, &hkKey);
236 if (E_FILENOTFOUND == hr)
237 {
238 ExitFunction1(hr = S_OK);
239 }
240 RegExitOnFailure(hr, "Failed to open this key for enumerating subkeys: %ls", wzSubKey);
241
242 // Yes, keep enumerating the 0th item, because we're deleting it every time
243 while (E_NOMOREITEMS != (hr = RegKeyEnum(hkKey, 0, &pszEnumeratedSubKey)))
244 {
245 RegExitOnFailure(hr, "Failed to enumerate key 0");
246
247 hr = PathConcat(wzSubKey, pszEnumeratedSubKey, &pszRecursiveSubKey);
248 RegExitOnFailure(hr, "Failed to concatenate paths while recursively deleting subkeys. Path1: %ls, Path2: %ls", wzSubKey, pszEnumeratedSubKey);
249
250 hr = RegDelete(hkRoot, pszRecursiveSubKey, kbKeyBitness, fDeleteTree);
251 RegExitOnFailure(hr, "Failed to recursively delete subkey: %ls", pszRecursiveSubKey);
252 }
253
254 hr = S_OK;
255 }
256
257 if (NULL != vpfnRegDeleteKeyExW)
258 {
259 er = vpfnRegDeleteKeyExW(hkRoot, wzSubKey, samDesired, 0);
260 if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er))
261 {
262 ExitFunction1(hr = E_FILENOTFOUND);
263 }
264 RegExitOnWin32Error(er, hr, "Failed to delete registry key (ex).");
265 }
266 else
267 {
268 er = vpfnRegDeleteKeyW(hkRoot, wzSubKey);
269 if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er))
270 {
271 ExitFunction1(hr = E_FILENOTFOUND);
272 }
273 RegExitOnWin32Error(er, hr, "Failed to delete registry key.");
274 }
275
276LExit:
277 ReleaseRegKey(hkKey);
278 ReleaseStr(pszEnumeratedSubKey);
279 ReleaseStr(pszRecursiveSubKey);
280
281 return hr;
282}
283
284
285/********************************************************************
286 RegKeyEnum - enumerates child registry keys.
287
288*********************************************************************/
289extern "C" HRESULT DAPI RegKeyEnum(
290 __in HKEY hk,
291 __in DWORD dwIndex,
292 __deref_out_z LPWSTR* psczKey
293 )
294{
295 HRESULT hr = S_OK;
296 DWORD er = ERROR_SUCCESS;
297 SIZE_T cb = 0;
298 DWORD cch = 0;
299
300 if (psczKey && *psczKey)
301 {
302 hr = StrMaxLength(*psczKey, &cb);
303 RegExitOnFailure(hr, "Failed to determine length of string.");
304
305 cch = (DWORD)min(DWORD_MAX, cb);
306 }
307
308 if (2 > cch)
309 {
310 cch = 2;
311
312 hr = StrAlloc(psczKey, cch);
313 RegExitOnFailure(hr, "Failed to allocate string to minimum size.");
314 }
315
316 er = vpfnRegEnumKeyExW(hk, dwIndex, *psczKey, &cch, NULL, NULL, NULL, NULL);
317 if (ERROR_MORE_DATA == er)
318 {
319 er = vpfnRegQueryInfoKeyW(hk, NULL, NULL, NULL, NULL, &cch, NULL, NULL, NULL, NULL, NULL, NULL);
320 RegExitOnWin32Error(er, hr, "Failed to get max size of subkey name under registry key.");
321
322 ++cch; // add one because RegQueryInfoKeyW() returns the length of the subkeys without the null terminator.
323 hr = StrAlloc(psczKey, cch);
324 RegExitOnFailure(hr, "Failed to allocate string bigger for enum registry key.");
325
326 er = vpfnRegEnumKeyExW(hk, dwIndex, *psczKey, &cch, NULL, NULL, NULL, NULL);
327 }
328 else if (ERROR_NO_MORE_ITEMS == er)
329 {
330 ExitFunction1(hr = E_NOMOREITEMS);
331 }
332 RegExitOnWin32Error(er, hr, "Failed to enum registry key.");
333
334 // Always ensure the registry key name is null terminated.
335#pragma prefast(push)
336#pragma prefast(disable:26018)
337 (*psczKey)[cch] = L'\0'; // note that cch will always be one less than the size of the buffer because that's how RegEnumKeyExW() works.
338#pragma prefast(pop)
339
340LExit:
341 return hr;
342}
343
344
345/********************************************************************
346 RegValueEnum - enumerates registry values.
347
348*********************************************************************/
349HRESULT DAPI RegValueEnum(
350 __in HKEY hk,
351 __in DWORD dwIndex,
352 __deref_out_z LPWSTR* psczName,
353 __out_opt DWORD *pdwType
354 )
355{
356 HRESULT hr = S_OK;
357 DWORD er = ERROR_SUCCESS;
358 DWORD cbValueName = 0;
359
360 er = vpfnRegQueryInfoKeyW(hk, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &cbValueName, NULL, NULL, NULL);
361 RegExitOnWin32Error(er, hr, "Failed to get max size of value name under registry key.");
362
363 // Add one for null terminator
364 ++cbValueName;
365
366 hr = StrAlloc(psczName, cbValueName);
367 RegExitOnFailure(hr, "Failed to allocate array for registry value name");
368
369 er = vpfnRegEnumValueW(hk, dwIndex, *psczName, &cbValueName, NULL, pdwType, NULL, NULL);
370 if (ERROR_NO_MORE_ITEMS == er)
371 {
372 ExitFunction1(hr = E_NOMOREITEMS);
373 }
374 RegExitOnWin32Error(er, hr, "Failed to enumerate registry value");
375
376LExit:
377 return hr;
378}
379
380/********************************************************************
381 RegGetType - reads a registry key value type.
382 *********************************************************************/
383HRESULT DAPI RegGetType(
384 __in HKEY hk,
385 __in_z_opt LPCWSTR wzName,
386 __out DWORD *pdwType
387 )
388{
389 HRESULT hr = S_OK;
390 DWORD er = ERROR_SUCCESS;
391
392 er = vpfnRegQueryValueExW(hk, wzName, NULL, pdwType, NULL, NULL);
393 if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er))
394 {
395 ExitFunction1(hr = E_FILENOTFOUND);
396 }
397 RegExitOnWin32Error(er, hr, "Failed to read registry value.");
398LExit:
399
400 return hr;
401}
402
403/********************************************************************
404 RegReadBinary - reads a registry key binary value.
405 NOTE: caller is responsible for freeing *ppbBuffer
406*********************************************************************/
407HRESULT DAPI RegReadBinary(
408 __in HKEY hk,
409 __in_z_opt LPCWSTR wzName,
410 __deref_out_bcount_opt(*pcbBuffer) BYTE** ppbBuffer,
411 __out SIZE_T *pcbBuffer
412 )
413{
414 HRESULT hr = S_OK;
415 LPBYTE pbBuffer = NULL;
416 DWORD er = ERROR_SUCCESS;
417 DWORD cb = 0;
418 DWORD dwType = 0;
419
420 er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, NULL, &cb);
421 RegExitOnWin32Error(er, hr, "Failed to get size of registry value.");
422
423 // Zero-length binary values can exist
424 if (0 < cb)
425 {
426 pbBuffer = static_cast<LPBYTE>(MemAlloc(cb, FALSE));
427 RegExitOnNull(pbBuffer, hr, E_OUTOFMEMORY, "Failed to allocate buffer for binary registry value.");
428
429 er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, pbBuffer, &cb);
430 if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er))
431 {
432 ExitFunction1(hr = E_FILENOTFOUND);
433 }
434 RegExitOnWin32Error(er, hr, "Failed to read registry value.");
435 }
436
437 if (REG_BINARY == dwType)
438 {
439 *ppbBuffer = pbBuffer;
440 pbBuffer = NULL;
441 *pcbBuffer = cb;
442 }
443 else
444 {
445 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE);
446 RegExitOnRootFailure(hr, "Error reading binary registry value due to unexpected data type: %u", dwType);
447 }
448
449LExit:
450 ReleaseMem(pbBuffer);
451
452 return hr;
453}
454
455
456/********************************************************************
457 RegReadString - reads a registry key value as a string.
458
459*********************************************************************/
460extern "C" HRESULT DAPI RegReadString(
461 __in HKEY hk,
462 __in_z_opt LPCWSTR wzName,
463 __deref_out_z LPWSTR* psczValue
464 )
465{
466 HRESULT hr = S_OK;
467 DWORD er = ERROR_SUCCESS;
468 SIZE_T cbValue = 0;
469 DWORD cch = 0;
470 DWORD cb = 0;
471 DWORD dwType = 0;
472 LPWSTR sczExpand = NULL;
473
474 if (psczValue && *psczValue)
475 {
476 hr = StrMaxLength(*psczValue, &cbValue);
477 RegExitOnFailure(hr, "Failed to determine length of string.");
478
479 cch = (DWORD)min(DWORD_MAX, cbValue);
480 }
481
482 if (2 > cch)
483 {
484 cch = 2;
485
486 hr = StrAlloc(psczValue, cch);
487 RegExitOnFailure(hr, "Failed to allocate string to minimum size.");
488 }
489
490 cb = sizeof(WCHAR) * (cch - 1); // subtract one to ensure there will be a space at the end of the string for the null terminator.
491 er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast<LPBYTE>(*psczValue), &cb);
492 if (ERROR_MORE_DATA == er)
493 {
494 cch = cb / sizeof(WCHAR) + 1; // add one to ensure there will be space at the end for the null terminator
495 hr = StrAlloc(psczValue, cch);
496 RegExitOnFailure(hr, "Failed to allocate string bigger for registry value.");
497
498 er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast<LPBYTE>(*psczValue), &cb);
499 }
500 if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er))
501 {
502 ExitFunction1(hr = E_FILENOTFOUND);
503 }
504 RegExitOnWin32Error(er, hr, "Failed to read registry key.");
505
506 if (REG_SZ == dwType || REG_EXPAND_SZ == dwType)
507 {
508 // Always ensure the registry value is null terminated.
509 (*psczValue)[cch - 1] = L'\0';
510
511 if (REG_EXPAND_SZ == dwType)
512 {
513 hr = StrAllocString(&sczExpand, *psczValue, 0);
514 RegExitOnFailure(hr, "Failed to copy registry value to expand.");
515
516 hr = PathExpand(psczValue, sczExpand, PATH_EXPAND_ENVIRONMENT);
517 RegExitOnFailure(hr, "Failed to expand registry value: %ls", *psczValue);
518 }
519 }
520 else
521 {
522 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE);
523 RegExitOnRootFailure(hr, "Error reading string registry value due to unexpected data type: %u", dwType);
524 }
525
526LExit:
527 ReleaseStr(sczExpand);
528
529 return hr;
530}
531
532
533/********************************************************************
534 RegReadStringArray - reads a registry key value REG_MULTI_SZ value as a string array.
535
536*********************************************************************/
537HRESULT DAPI RegReadStringArray(
538 __in HKEY hk,
539 __in_z_opt LPCWSTR wzName,
540 __deref_out_ecount_opt(*pcStrings) LPWSTR** prgsczStrings,
541 __out DWORD *pcStrings
542 )
543{
544 HRESULT hr = S_OK;
545 DWORD er = ERROR_SUCCESS;
546 DWORD dwNullCharacters = 0;
547 DWORD dwType = 0;
548 DWORD cb = 0;
549 DWORD cch = 0;
550 LPCWSTR wzSource = NULL;
551 LPWSTR sczValue = NULL;
552
553 er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast<LPBYTE>(sczValue), &cb);
554 if (0 < cb)
555 {
556 cch = cb / sizeof(WCHAR);
557 hr = StrAlloc(&sczValue, cch);
558 RegExitOnFailure(hr, "Failed to allocate string for registry value.");
559
560 er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast<LPBYTE>(sczValue), &cb);
561 }
562 if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er))
563 {
564 ExitFunction1(hr = E_FILENOTFOUND);
565 }
566 RegExitOnWin32Error(er, hr, "Failed to read registry key.");
567
568 if (cb / sizeof(WCHAR) != cch)
569 {
570 hr = E_UNEXPECTED;
571 RegExitOnFailure(hr, "The size of registry value %ls unexpected changed between 2 reads", wzName);
572 }
573
574 if (REG_MULTI_SZ != dwType)
575 {
576 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE);
577 RegExitOnRootFailure(hr, "Tried to read string array, but registry value %ls is of an incorrect type", wzName);
578 }
579
580 // Value exists, but is empty, so no strings to return.
581 if (2 > cch)
582 {
583 *prgsczStrings = NULL;
584 *pcStrings = 0;
585 ExitFunction1(hr = S_OK);
586 }
587
588 // The docs specifically say if the value was written without double-null-termination, it'll get read back without it too.
589 if (L'\0' != sczValue[cch-1] || L'\0' != sczValue[cch-2])
590 {
591 hr = E_INVALIDARG;
592 RegExitOnFailure(hr, "Tried to read string array, but registry value %ls is invalid (isn't double-null-terminated)", wzName);
593 }
594
595 cch = cb / sizeof(WCHAR);
596 for (DWORD i = 0; i < cch; ++i)
597 {
598 if (L'\0' == sczValue[i])
599 {
600 ++dwNullCharacters;
601 }
602 }
603
604 // There's one string for every null character encountered (except the extra 1 at the end of the string)
605 *pcStrings = dwNullCharacters - 1;
606 hr = MemEnsureArraySize(reinterpret_cast<LPVOID *>(prgsczStrings), *pcStrings, sizeof(LPWSTR), 0);
607 RegExitOnFailure(hr, "Failed to resize array while reading REG_MULTI_SZ value");
608
609#pragma prefast(push)
610#pragma prefast(disable:26010)
611 wzSource = sczValue;
612 for (DWORD i = 0; i < *pcStrings; ++i)
613 {
614 hr = StrAllocString(&(*prgsczStrings)[i], wzSource, 0);
615 RegExitOnFailure(hr, "Failed to allocate copy of string");
616
617 // Skip past this string
618 wzSource += lstrlenW(wzSource) + 1;
619 }
620#pragma prefast(pop)
621
622LExit:
623 ReleaseStr(sczValue);
624
625 return hr;
626}
627
628
629/********************************************************************
630 RegReadVersion - reads a registry key value as a version.
631
632*********************************************************************/
633extern "C" HRESULT DAPI RegReadVersion(
634 __in HKEY hk,
635 __in_z_opt LPCWSTR wzName,
636 __out DWORD64* pdw64Version
637 )
638{
639 HRESULT hr = S_OK;
640 DWORD er = ERROR_SUCCESS;
641 DWORD dwType = 0;
642 DWORD cb = 0;
643 LPWSTR sczVersion = NULL;
644
645 cb = sizeof(DWORD64);
646 er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast<LPBYTE>(*pdw64Version), &cb);
647 if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er))
648 {
649 ExitFunction1(hr = E_FILENOTFOUND);
650 }
651 if (REG_SZ == dwType || REG_EXPAND_SZ == dwType)
652 {
653 hr = RegReadString(hk, wzName, &sczVersion);
654 RegExitOnFailure(hr, "Failed to read registry version as string.");
655
656 hr = FileVersionFromStringEx(sczVersion, 0, pdw64Version);
657 RegExitOnFailure(hr, "Failed to convert registry string to version.");
658 }
659 else if (REG_QWORD == dwType)
660 {
661 RegExitOnWin32Error(er, hr, "Failed to read registry key.");
662 }
663 else // unexpected data type
664 {
665 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE);
666 RegExitOnRootFailure(hr, "Error reading version registry value due to unexpected data type: %u", dwType);
667 }
668
669LExit:
670 ReleaseStr(sczVersion);
671
672 return hr;
673}
674
675
676/********************************************************************
677 RegReadNumber - reads a DWORD registry key value as a number.
678
679*********************************************************************/
680extern "C" HRESULT DAPI RegReadNumber(
681 __in HKEY hk,
682 __in_z_opt LPCWSTR wzName,
683 __out DWORD* pdwValue
684 )
685{
686 HRESULT hr = S_OK;
687 DWORD er = ERROR_SUCCESS;
688 DWORD dwType = 0;
689 DWORD cb = sizeof(DWORD);
690
691 er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast<LPBYTE>(pdwValue), &cb);
692 if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er))
693 {
694 ExitFunction1(hr = E_FILENOTFOUND);
695 }
696 RegExitOnWin32Error(er, hr, "Failed to query registry key value.");
697
698 if (REG_DWORD != dwType)
699 {
700 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE);
701 RegExitOnRootFailure(hr, "Error reading version registry value due to unexpected data type: %u", dwType);
702 }
703
704LExit:
705 return hr;
706}
707
708
709/********************************************************************
710 RegReadQword - reads a QWORD registry key value as a number.
711
712*********************************************************************/
713extern "C" HRESULT DAPI RegReadQword(
714 __in HKEY hk,
715 __in_z_opt LPCWSTR wzName,
716 __out DWORD64* pqwValue
717 )
718{
719 HRESULT hr = S_OK;
720 DWORD er = ERROR_SUCCESS;
721 DWORD dwType = 0;
722 DWORD cb = sizeof(DWORD64);
723
724 er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast<LPBYTE>(pqwValue), &cb);
725 if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er))
726 {
727 ExitFunction1(hr = E_FILENOTFOUND);
728 }
729 RegExitOnWin32Error(er, hr, "Failed to query registry key value.");
730
731 if (REG_QWORD != dwType)
732 {
733 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE);
734 RegExitOnRootFailure(hr, "Error reading version registry value due to unexpected data type: %u", dwType);
735 }
736
737LExit:
738 return hr;
739}
740
741
742/********************************************************************
743 RegWriteBinary - writes a registry key value as a binary.
744
745*********************************************************************/
746HRESULT DAPI RegWriteBinary(
747 __in HKEY hk,
748 __in_z_opt LPCWSTR wzName,
749 __in_bcount(cbBuffer) const BYTE *pbBuffer,
750 __in DWORD cbBuffer
751 )
752{
753 HRESULT hr = S_OK;
754 DWORD er = ERROR_SUCCESS;
755
756 er = vpfnRegSetValueExW(hk, wzName, 0, REG_BINARY, pbBuffer, cbBuffer);
757 RegExitOnWin32Error(er, hr, "Failed to write binary registry value with name: %ls", wzName);
758
759LExit:
760 return hr;
761}
762
763
764/********************************************************************
765RegWriteExpandString - writes a registry key value as an expand string.
766
767Note: if wzValue is NULL the value will be removed.
768*********************************************************************/
769extern "C" HRESULT DAPI RegWriteExpandString(
770 __in HKEY hk,
771 __in_z_opt LPCWSTR wzName,
772 __in_z_opt LPCWSTR wzValue
773)
774{
775 return WriteStringToRegistry(hk, wzName, wzValue, REG_EXPAND_SZ);
776}
777
778
779/********************************************************************
780 RegWriteString - writes a registry key value as a string.
781
782 Note: if wzValue is NULL the value will be removed.
783*********************************************************************/
784extern "C" HRESULT DAPI RegWriteString(
785 __in HKEY hk,
786 __in_z_opt LPCWSTR wzName,
787 __in_z_opt LPCWSTR wzValue
788 )
789{
790 return WriteStringToRegistry(hk, wzName, wzValue, REG_SZ);
791}
792
793
794/********************************************************************
795 RegWriteStringFormatted - writes a registry key value as a formatted string.
796
797*********************************************************************/
798extern "C" HRESULT DAPI RegWriteStringFormatted(
799 __in HKEY hk,
800 __in_z_opt LPCWSTR wzName,
801 __in __format_string LPCWSTR szFormat,
802 ...
803 )
804{
805 HRESULT hr = S_OK;
806 LPWSTR sczValue = NULL;
807 va_list args;
808
809 va_start(args, szFormat);
810 hr = StrAllocFormattedArgs(&sczValue, szFormat, args);
811 va_end(args);
812 RegExitOnFailure(hr, "Failed to allocate %ls value.", wzName);
813
814 hr = WriteStringToRegistry(hk, wzName, sczValue, REG_SZ);
815
816LExit:
817 ReleaseStr(sczValue);
818
819 return hr;
820}
821
822
823/********************************************************************
824 RegWriteStringArray - writes an array of strings as a REG_MULTI_SZ value
825
826*********************************************************************/
827HRESULT DAPI RegWriteStringArray(
828 __in HKEY hk,
829 __in_z_opt LPCWSTR wzName,
830 __in_ecount(cValues) LPWSTR *rgwzValues,
831 __in DWORD cValues
832 )
833{
834 HRESULT hr = S_OK;
835 DWORD er = ERROR_SUCCESS;
836 LPWSTR wzCopyDestination = NULL;
837 LPCWSTR wzWriteValue = NULL;
838 LPWSTR sczWriteValue = NULL;
839 DWORD dwTotalStringSize = 0;
840 DWORD cbTotalStringSize = 0;
841 DWORD dwTemp = 0;
842
843 if (0 == cValues)
844 {
845 wzWriteValue = L"\0";
846 }
847 else
848 {
849 // Add space for the null terminator
850 dwTotalStringSize = 1;
851
852 for (DWORD i = 0; i < cValues; ++i)
853 {
854 dwTemp = dwTotalStringSize;
855 hr = ::DWordAdd(dwTemp, 1 + lstrlenW(rgwzValues[i]), &dwTotalStringSize);
856 RegExitOnFailure(hr, "DWORD Overflow while adding length of string to write REG_MULTI_SZ");
857 }
858
859 hr = StrAlloc(&sczWriteValue, dwTotalStringSize);
860 RegExitOnFailure(hr, "Failed to allocate space for string while writing REG_MULTI_SZ");
861
862 wzCopyDestination = sczWriteValue;
863 dwTemp = dwTotalStringSize;
864 for (DWORD i = 0; i < cValues; ++i)
865 {
866 hr = ::StringCchCopyW(wzCopyDestination, dwTotalStringSize, rgwzValues[i]);
867 RegExitOnFailure(hr, "failed to copy string: %ls", rgwzValues[i]);
868
869 dwTemp -= lstrlenW(rgwzValues[i]) + 1;
870 wzCopyDestination += lstrlenW(rgwzValues[i]) + 1;
871 }
872
873 wzWriteValue = sczWriteValue;
874 }
875
876 hr = ::DWordMult(dwTotalStringSize, sizeof(WCHAR), &cbTotalStringSize);
877 RegExitOnFailure(hr, "Failed to get total string size in bytes");
878
879 er = vpfnRegSetValueExW(hk, wzName, 0, REG_MULTI_SZ, reinterpret_cast<const BYTE *>(wzWriteValue), cbTotalStringSize);
880 RegExitOnWin32Error(er, hr, "Failed to set registry value to array of strings (first string of which is): %ls", wzWriteValue);
881
882LExit:
883 ReleaseStr(sczWriteValue);
884
885 return hr;
886}
887
888/********************************************************************
889 RegWriteNumber - writes a registry key value as a number.
890
891*********************************************************************/
892extern "C" HRESULT DAPI RegWriteNumber(
893 __in HKEY hk,
894 __in_z_opt LPCWSTR wzName,
895 __in DWORD dwValue
896 )
897{
898 HRESULT hr = S_OK;
899 DWORD er = ERROR_SUCCESS;
900
901 er = vpfnRegSetValueExW(hk, wzName, 0, REG_DWORD, reinterpret_cast<const BYTE *>(&dwValue), sizeof(dwValue));
902 RegExitOnWin32Error(er, hr, "Failed to set %ls value.", wzName);
903
904LExit:
905 return hr;
906}
907
908/********************************************************************
909 RegWriteQword - writes a registry key value as a Qword.
910
911*********************************************************************/
912extern "C" HRESULT DAPI RegWriteQword(
913 __in HKEY hk,
914 __in_z_opt LPCWSTR wzName,
915 __in DWORD64 qwValue
916 )
917{
918 HRESULT hr = S_OK;
919 DWORD er = ERROR_SUCCESS;
920
921 er = vpfnRegSetValueExW(hk, wzName, 0, REG_QWORD, reinterpret_cast<const BYTE *>(&qwValue), sizeof(qwValue));
922 RegExitOnWin32Error(er, hr, "Failed to set %ls value.", wzName);
923
924LExit:
925 return hr;
926}
927
928/********************************************************************
929 RegQueryKey - queries the key for the number of subkeys and values.
930
931*********************************************************************/
932extern "C" HRESULT DAPI RegQueryKey(
933 __in HKEY hk,
934 __out_opt DWORD* pcSubKeys,
935 __out_opt DWORD* pcValues
936 )
937{
938 HRESULT hr = S_OK;
939 DWORD er = ERROR_SUCCESS;
940
941 er = vpfnRegQueryInfoKeyW(hk, NULL, NULL, NULL, pcSubKeys, NULL, NULL, pcValues, NULL, NULL, NULL, NULL);
942 RegExitOnWin32Error(er, hr, "Failed to get the number of subkeys and values under registry key.");
943
944LExit:
945 return hr;
946}
947
948/********************************************************************
949RegKeyReadNumber - reads a DWORD registry key value as a number from
950a specified subkey.
951
952*********************************************************************/
953extern "C" HRESULT DAPI RegKeyReadNumber(
954 __in HKEY hk,
955 __in_z LPCWSTR wzSubKey,
956 __in_z_opt LPCWSTR wzName,
957 __in BOOL f64Bit,
958 __out DWORD* pdwValue
959 )
960{
961 HRESULT hr = S_OK;
962 HKEY hkKey = NULL;
963
964 hr = RegOpen(hk, wzSubKey, KEY_READ | f64Bit ? KEY_WOW64_64KEY : 0, &hkKey);
965 RegExitOnFailure(hr, "Failed to open key: %ls", wzSubKey);
966
967 hr = RegReadNumber(hkKey, wzName, pdwValue);
968 RegExitOnFailure(hr, "Failed to read value: %ls/@%ls", wzSubKey, wzName);
969
970LExit:
971 ReleaseRegKey(hkKey);
972
973 return hr;
974}
975
976/********************************************************************
977RegValueExists - determines whether a named value exists in a
978specified subkey.
979
980*********************************************************************/
981extern "C" BOOL DAPI RegValueExists(
982 __in HKEY hk,
983 __in_z LPCWSTR wzSubKey,
984 __in_z_opt LPCWSTR wzName,
985 __in BOOL f64Bit
986 )
987{
988 HRESULT hr = S_OK;
989 HKEY hkKey = NULL;
990 DWORD dwType = 0;
991
992 hr = RegOpen(hk, wzSubKey, KEY_READ | f64Bit ? KEY_WOW64_64KEY : 0, &hkKey);
993 RegExitOnFailure(hr, "Failed to open key: %ls", wzSubKey);
994
995 hr = RegGetType(hkKey, wzName, &dwType);
996 RegExitOnFailure(hr, "Failed to read value type: %ls/@%ls", wzSubKey, wzName);
997
998LExit:
999 ReleaseRegKey(hkKey);
1000
1001 return SUCCEEDED(hr);
1002}
1003
1004static HRESULT WriteStringToRegistry(
1005 __in HKEY hk,
1006 __in_z_opt LPCWSTR wzName,
1007 __in_z_opt LPCWSTR wzValue,
1008 __in DWORD dwType
1009 )
1010{
1011 HRESULT hr = S_OK;
1012 DWORD er = ERROR_SUCCESS;
1013 size_t cbValue = 0;
1014
1015 if (wzValue)
1016 {
1017 hr = ::StringCbLengthW(wzValue, STRSAFE_MAX_CCH * sizeof(TCHAR), &cbValue);
1018 RegExitOnFailure(hr, "Failed to determine length of registry value: %ls", wzName);
1019
1020 er = vpfnRegSetValueExW(hk, wzName, 0, dwType, reinterpret_cast<const BYTE *>(wzValue), static_cast<DWORD>(cbValue));
1021 RegExitOnWin32Error(er, hr, "Failed to set registry value: %ls", wzName);
1022 }
1023 else
1024 {
1025 er = vpfnRegDeleteValueW(hk, wzName);
1026 if (ERROR_FILE_NOT_FOUND == er || ERROR_PATH_NOT_FOUND == er)
1027 {
1028 er = ERROR_SUCCESS;
1029 }
1030 RegExitOnWin32Error(er, hr, "Failed to delete registry value: %ls", wzName);
1031 }
1032
1033LExit:
1034 return hr;
1035}
diff --git a/src/libs/dutil/WixToolset.DUtil/resrutil.cpp b/src/libs/dutil/WixToolset.DUtil/resrutil.cpp
new file mode 100644
index 00000000..a6a7ee23
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/resrutil.cpp
@@ -0,0 +1,266 @@
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// Exit macros
7#define ResrExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_RESRUTIL, x, s, __VA_ARGS__)
8#define ResrExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_RESRUTIL, x, s, __VA_ARGS__)
9#define ResrExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_RESRUTIL, x, s, __VA_ARGS__)
10#define ResrExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_RESRUTIL, x, s, __VA_ARGS__)
11#define ResrExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_RESRUTIL, x, s, __VA_ARGS__)
12#define ResrExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_RESRUTIL, x, s, __VA_ARGS__)
13#define ResrExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_RESRUTIL, p, x, e, s, __VA_ARGS__)
14#define ResrExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_RESRUTIL, p, x, s, __VA_ARGS__)
15#define ResrExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_RESRUTIL, p, x, e, s, __VA_ARGS__)
16#define ResrExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_RESRUTIL, p, x, s, __VA_ARGS__)
17#define ResrExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_RESRUTIL, e, x, s, __VA_ARGS__)
18#define ResrExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_RESRUTIL, g, x, s, __VA_ARGS__)
19
20#define RES_STRINGS_PER_BLOCK 16
21
22
23BOOL CALLBACK EnumLangIdProc(
24 __in_opt HMODULE hModule,
25 __in_z LPCSTR lpType,
26 __in_z LPCSTR lpName,
27 __in WORD wLanguage,
28 __in LONG_PTR lParam
29 );
30
31/********************************************************************
32ResGetStringLangId - get the language id for a string in the string table.
33
34********************************************************************/
35extern "C" HRESULT DAPI ResGetStringLangId(
36 __in_opt LPCWSTR wzPath,
37 __in UINT uID,
38 __out WORD *pwLangId
39 )
40{
41 Assert(pwLangId);
42
43 HRESULT hr = S_OK;
44 HINSTANCE hModule = NULL;
45 DWORD dwBlockId = (uID / RES_STRINGS_PER_BLOCK) + 1;
46 WORD wFoundLangId = 0;
47
48 if (wzPath && *wzPath)
49 {
50 hModule = LoadLibraryExW(wzPath, NULL, DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE);
51 ResrExitOnNullWithLastError(hModule, hr, "Failed to open resource file: %ls", wzPath);
52 }
53
54#pragma prefast(push)
55#pragma prefast(disable:25068)
56 if (!::EnumResourceLanguagesA(hModule, RT_STRING, MAKEINTRESOURCE(dwBlockId), static_cast<ENUMRESLANGPROC>(EnumLangIdProc), reinterpret_cast<LONG_PTR>(&wFoundLangId)))
57#pragma prefast(pop)
58 {
59 ResrExitWithLastError(hr, "Failed to find string language identifier.");
60 }
61
62 *pwLangId = wFoundLangId;
63
64LExit:
65 if (hModule)
66 {
67 ::FreeLibrary(hModule);
68 }
69
70 return hr;
71}
72
73
74/********************************************************************
75ResReadString
76
77NOTE: ppwzString should be freed with StrFree()
78********************************************************************/
79extern "C" HRESULT DAPI ResReadString(
80 __in HINSTANCE hinst,
81 __in UINT uID,
82 __deref_out_z LPWSTR* ppwzString
83 )
84{
85 Assert(hinst && ppwzString);
86
87 HRESULT hr = S_OK;
88 DWORD cch = 64; // first guess
89 DWORD cchReturned = 0;
90
91 do
92 {
93 hr = StrAlloc(ppwzString, cch);
94 ResrExitOnFailureDebugTrace(hr, "Failed to allocate string for resource id: %d", uID);
95
96 cchReturned = ::LoadStringW(hinst, uID, *ppwzString, cch);
97 if (0 == cchReturned)
98 {
99 ResrExitWithLastError(hr, "Failed to load string resource id: %d", uID);
100 }
101
102 // if the returned string count is one character too small, it's likely we have
103 // more data to read
104 if (cchReturned + 1 == cch)
105 {
106 cch *= 2;
107 hr = S_FALSE;
108 }
109 } while (S_FALSE == hr);
110 ResrExitOnFailure(hr, "Failed to load string resource id: %d", uID);
111
112LExit:
113 return hr;
114}
115
116
117/********************************************************************
118 ResReadStringAnsi
119
120 NOTE: ppszString should be freed with StrFree()
121********************************************************************/
122extern "C" HRESULT DAPI ResReadStringAnsi(
123 __in HINSTANCE hinst,
124 __in UINT uID,
125 __deref_out_z LPSTR* ppszString
126 )
127{
128 Assert(hinst && ppszString);
129
130 HRESULT hr = S_OK;
131 DWORD cch = 64; // first guess
132 DWORD cchReturned = 0;
133
134 do
135 {
136 hr = StrAnsiAlloc(ppszString, cch);
137 ResrExitOnFailureDebugTrace(hr, "Failed to allocate string for resource id: %d", uID);
138
139#pragma prefast(push)
140#pragma prefast(disable:25068)
141 cchReturned = ::LoadStringA(hinst, uID, *ppszString, cch);
142#pragma prefast(pop)
143 if (0 == cchReturned)
144 {
145 ResrExitWithLastError(hr, "Failed to load string resource id: %d", uID);
146 }
147
148 // if the returned string count is one character too small, it's likely we have
149 // more data to read
150 if (cchReturned + 1 == cch)
151 {
152 cch *= 2;
153 hr = S_FALSE;
154 }
155 } while (S_FALSE == hr);
156 ResrExitOnFailure(hr, "failed to load string resource id: %d", uID);
157
158LExit:
159 return hr;
160}
161
162
163/********************************************************************
164ResReadData - returns a pointer to the specified resource data
165
166NOTE: there is no "free" function for this call
167********************************************************************/
168extern "C" HRESULT DAPI ResReadData(
169 __in_opt HINSTANCE hinst,
170 __in_z LPCSTR szDataName,
171 __deref_out_bcount(*pcb) PVOID *ppv,
172 __out DWORD *pcb
173 )
174{
175 Assert(szDataName);
176 Assert(ppv);
177
178 HRESULT hr = S_OK;
179 HRSRC hRsrc = NULL;
180 HGLOBAL hData = NULL;
181 DWORD cbData = 0;
182
183#pragma prefast(push)
184#pragma prefast(disable:25068)
185 hRsrc = ::FindResourceExA(hinst, RT_RCDATA, szDataName, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL));
186#pragma prefast(pop)
187 ResrExitOnNullWithLastError(hRsrc, hr, "Failed to find resource.");
188
189 hData = ::LoadResource(hinst, hRsrc);
190 ResrExitOnNullWithLastError(hData, hr, "Failed to load resource.");
191
192 cbData = ::SizeofResource(hinst, hRsrc);
193 if (!cbData)
194 {
195 ResrExitWithLastError(hr, "Failed to get size of resource.");
196 }
197
198 *ppv = ::LockResource(hData);
199 ResrExitOnNullWithLastError(*ppv, hr, "Failed to lock data resource.");
200 *pcb = cbData;
201
202LExit:
203 return hr;
204}
205
206
207/********************************************************************
208ResExportDataToFile - extracts the resource data to the specified target file
209
210********************************************************************/
211extern "C" HRESULT DAPI ResExportDataToFile(
212 __in_z LPCSTR szDataName,
213 __in_z LPCWSTR wzTargetFile,
214 __in DWORD dwCreationDisposition
215 )
216{
217 HRESULT hr = S_OK;
218 PVOID pData = NULL;
219 DWORD cbData = 0;
220 DWORD cbWritten = 0;
221 HANDLE hFile = INVALID_HANDLE_VALUE;
222 BOOL bCreatedFile = FALSE;
223
224 hr = ResReadData(NULL, szDataName, &pData, &cbData);
225 ResrExitOnFailure(hr, "Failed to GetData from %s.", szDataName);
226
227 hFile = ::CreateFileW(wzTargetFile, GENERIC_WRITE, 0, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL);
228 if (INVALID_HANDLE_VALUE == hFile)
229 {
230 ResrExitWithLastError(hr, "Failed to CreateFileW for %ls.", wzTargetFile);
231 }
232 bCreatedFile = TRUE;
233
234 if (!::WriteFile(hFile, pData, cbData, &cbWritten, NULL))
235 {
236 ResrExitWithLastError(hr, "Failed to ::WriteFile for %ls.", wzTargetFile);
237 }
238
239LExit:
240 ReleaseFile(hFile);
241
242 if (FAILED(hr))
243 {
244 if (bCreatedFile)
245 {
246 ::DeleteFileW(wzTargetFile);
247 }
248 }
249
250 return hr;
251}
252
253
254BOOL CALLBACK EnumLangIdProc(
255 __in_opt HMODULE /* hModule */,
256 __in_z LPCSTR /* lpType */,
257 __in_z LPCSTR /* lpName */,
258 __in WORD wLanguage,
259 __in LONG_PTR lParam
260 )
261{
262 WORD *pwLangId = reinterpret_cast<WORD*>(lParam);
263
264 *pwLangId = wLanguage;
265 return TRUE;
266}
diff --git a/src/libs/dutil/WixToolset.DUtil/reswutil.cpp b/src/libs/dutil/WixToolset.DUtil/reswutil.cpp
new file mode 100644
index 00000000..e78de84a
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/reswutil.cpp
@@ -0,0 +1,386 @@
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// Exit macros
7#define ReswExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_RESWUTIL, x, s, __VA_ARGS__)
8#define ReswExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_RESWUTIL, x, s, __VA_ARGS__)
9#define ReswExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_RESWUTIL, x, s, __VA_ARGS__)
10#define ReswExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_RESWUTIL, x, s, __VA_ARGS__)
11#define ReswExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_RESWUTIL, x, s, __VA_ARGS__)
12#define ReswExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_RESWUTIL, x, s, __VA_ARGS__)
13#define ReswExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_RESWUTIL, p, x, e, s, __VA_ARGS__)
14#define ReswExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_RESWUTIL, p, x, s, __VA_ARGS__)
15#define ReswExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_RESWUTIL, p, x, e, s, __VA_ARGS__)
16#define ReswExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_RESWUTIL, p, x, s, __VA_ARGS__)
17#define ReswExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_RESWUTIL, e, x, s, __VA_ARGS__)
18#define ReswExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_RESWUTIL, g, x, s, __VA_ARGS__)
19
20#define RES_STRINGS_PER_BLOCK 16
21
22// Internal data structure format for a string block in a resource table.
23// Note: Strings are always stored as UNICODED.
24typedef struct _RES_STRING_BLOCK
25{
26 DWORD dwBlockId;
27 WORD wLangId;
28 LPWSTR rgwz[RES_STRINGS_PER_BLOCK];
29} RES_STRING_BLOCK;
30
31
32// private functions
33static HRESULT StringBlockInitialize(
34 __in_opt HINSTANCE hModule,
35 __in DWORD dwBlockId,
36 __in WORD wLangId,
37 __in RES_STRING_BLOCK* pStrBlock
38 );
39static void StringBlockUnitialize(
40 __in RES_STRING_BLOCK* pStrBlock
41 );
42static HRESULT StringBlockChangeString(
43 __in RES_STRING_BLOCK* pStrBlock,
44 __in DWORD dwStringId,
45 __in_z LPCWSTR szData
46 );
47static HRESULT StringBlockConvertToResourceData(
48 __in const RES_STRING_BLOCK* pStrBlock,
49 __deref_out_bcount(*pcbData) LPVOID* ppvData,
50 __out DWORD* pcbData
51 );
52static HRESULT StringBlockConvertFromResourceData(
53 __in RES_STRING_BLOCK* pStrBlock,
54 __in_bcount(cbData) LPCVOID pvData,
55 __in SIZE_T cbData
56 );
57
58
59/********************************************************************
60ResWriteString - sets the string into to the specified file's resource name
61
62********************************************************************/
63extern "C" HRESULT DAPI ResWriteString(
64 __in_z LPCWSTR wzResourceFile,
65 __in DWORD dwDataId,
66 __in_z LPCWSTR wzData,
67 __in WORD wLangId
68 )
69{
70 Assert(wzResourceFile);
71 Assert(wzData);
72
73 HRESULT hr = S_OK;
74 HINSTANCE hModule = NULL;
75 HANDLE hUpdate = NULL;
76 RES_STRING_BLOCK StrBlock = { };
77 LPVOID pvData = NULL;
78 DWORD cbData = 0;
79
80 DWORD dwBlockId = (dwDataId / RES_STRINGS_PER_BLOCK) + 1;
81 DWORD dwStringId = (dwDataId % RES_STRINGS_PER_BLOCK);
82
83 hModule = LoadLibraryExW(wzResourceFile, NULL, DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE);
84 ReswExitOnNullWithLastError(hModule, hr, "Failed to load library: %ls", wzResourceFile);
85
86 hr = StringBlockInitialize(hModule, dwBlockId, wLangId, &StrBlock);
87 ReswExitOnFailure(hr, "Failed to get string block to update.");
88
89 hr = StringBlockChangeString(&StrBlock, dwStringId, wzData);
90 ReswExitOnFailure(hr, "Failed to update string block string.");
91
92 hr = StringBlockConvertToResourceData(&StrBlock, &pvData, &cbData);
93 ReswExitOnFailure(hr, "Failed to convert string block to resource data.");
94
95 ::FreeLibrary(hModule);
96 hModule = NULL;
97
98 hUpdate = ::BeginUpdateResourceW(wzResourceFile, FALSE);
99 ReswExitOnNullWithLastError(hUpdate, hr, "Failed to ::BeginUpdateResourcesW.");
100
101 if (!::UpdateResourceA(hUpdate, RT_STRING, MAKEINTRESOURCE(dwBlockId), wLangId, pvData, cbData))
102 {
103 ReswExitWithLastError(hr, "Failed to ::UpdateResourceA.");
104 }
105
106 if (!::EndUpdateResource(hUpdate, FALSE))
107 {
108 ReswExitWithLastError(hr, "Failed to ::EndUpdateResourceW.");
109 }
110
111 hUpdate = NULL;
112
113LExit:
114 ReleaseMem(pvData);
115
116 StringBlockUnitialize(&StrBlock);
117
118 if (hUpdate)
119 {
120 ::EndUpdateResource(hUpdate, TRUE);
121 }
122
123 if (hModule)
124 {
125 ::FreeLibrary(hModule);
126 }
127
128 return hr;
129}
130
131
132/********************************************************************
133ResWriteData - sets the data into to the specified file's resource name
134
135********************************************************************/
136extern "C" HRESULT DAPI ResWriteData(
137 __in_z LPCWSTR wzResourceFile,
138 __in_z LPCSTR szDataName,
139 __in PVOID pData,
140 __in DWORD cbData
141 )
142{
143 Assert(wzResourceFile);
144 Assert(szDataName);
145 Assert(pData);
146 Assert(cbData);
147
148 HRESULT hr = S_OK;
149 HANDLE hUpdate = NULL;
150
151 hUpdate = ::BeginUpdateResourceW(wzResourceFile, FALSE);
152 ReswExitOnNullWithLastError(hUpdate, hr, "Failed to ::BeginUpdateResourcesW.");
153
154 if (!::UpdateResourceA(hUpdate, RT_RCDATA, szDataName, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), pData, cbData))
155 {
156 ReswExitWithLastError(hr, "Failed to ::UpdateResourceA.");
157 }
158
159 if (!::EndUpdateResource(hUpdate, FALSE))
160 {
161 ReswExitWithLastError(hr, "Failed to ::EndUpdateResourceW.");
162 }
163
164 hUpdate = NULL;
165
166LExit:
167 if (hUpdate)
168 {
169 ::EndUpdateResource(hUpdate, TRUE);
170 }
171
172 return hr;
173}
174
175
176/********************************************************************
177ResImportDataFromFile - reads a file and sets the data into to the specified file's resource name
178
179********************************************************************/
180extern "C" HRESULT DAPI ResImportDataFromFile(
181 __in_z LPCWSTR wzTargetFile,
182 __in_z LPCWSTR wzSourceFile,
183 __in_z LPCSTR szDataName
184 )
185{
186 HRESULT hr = S_OK;
187 HANDLE hFile = INVALID_HANDLE_VALUE;
188 DWORD cbFile = 0;
189 HANDLE hMap = NULL;
190 PVOID pv = NULL;
191
192 hFile = ::CreateFileW(wzSourceFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
193 if (INVALID_HANDLE_VALUE == hFile)
194 {
195 ReswExitWithLastError(hr, "Failed to CreateFileW for %ls.", wzSourceFile);
196 }
197
198 cbFile = ::GetFileSize(hFile, NULL);
199 if (!cbFile)
200 {
201 ReswExitWithLastError(hr, "Failed to GetFileSize for %ls.", wzSourceFile);
202 }
203
204 hMap = ::CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
205 ReswExitOnNullWithLastError(hMap, hr, "Failed to CreateFileMapping for %ls.", wzSourceFile);
206
207 pv = ::MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, cbFile);
208 ReswExitOnNullWithLastError(pv, hr, "Failed to MapViewOfFile for %ls.", wzSourceFile);
209
210 hr = ResWriteData(wzTargetFile, szDataName, pv, cbFile);
211 ReswExitOnFailure(hr, "Failed to ResSetData %s on file %ls.", szDataName, wzTargetFile);
212
213LExit:
214 if (pv)
215 {
216 ::UnmapViewOfFile(pv);
217 }
218
219 if (hMap)
220 {
221 ::CloseHandle(hMap);
222 }
223
224 ReleaseFile(hFile);
225
226 return hr;
227}
228
229
230static HRESULT StringBlockInitialize(
231 __in_opt HINSTANCE hModule,
232 __in DWORD dwBlockId,
233 __in WORD wLangId,
234 __in RES_STRING_BLOCK* pStrBlock
235 )
236{
237 HRESULT hr = S_OK;
238 HRSRC hRsrc = NULL;
239 HGLOBAL hData = NULL;
240 LPCVOID pvData = NULL; // does not need to be freed
241 DWORD cbData = 0;
242
243 hRsrc = ::FindResourceExA(hModule, RT_STRING, MAKEINTRESOURCE(dwBlockId), wLangId);
244 ReswExitOnNullWithLastError(hRsrc, hr, "Failed to ::FindResourceExW.");
245
246 hData = ::LoadResource(hModule, hRsrc);
247 ReswExitOnNullWithLastError(hData, hr, "Failed to ::LoadResource.");
248
249 cbData = ::SizeofResource(hModule, hRsrc);
250 if (!cbData)
251 {
252 ReswExitWithLastError(hr, "Failed to ::SizeofResource.");
253 }
254
255 pvData = ::LockResource(hData);
256 ReswExitOnNullWithLastError(pvData, hr, "Failed to lock data resource.");
257
258 pStrBlock->dwBlockId = dwBlockId;
259 pStrBlock->wLangId = wLangId;
260
261 hr = StringBlockConvertFromResourceData(pStrBlock, pvData, cbData);
262 ReswExitOnFailure(hr, "Failed to convert string block from resource data.");
263
264LExit:
265 return hr;
266}
267
268
269static void StringBlockUnitialize(
270 __in RES_STRING_BLOCK* pStrBlock
271 )
272{
273 if (pStrBlock)
274 {
275 for (DWORD i = 0; i < RES_STRINGS_PER_BLOCK; ++i)
276 {
277 ReleaseNullMem(pStrBlock->rgwz[i]);
278 }
279 }
280}
281
282
283static HRESULT StringBlockChangeString(
284 __in RES_STRING_BLOCK* pStrBlock,
285 __in DWORD dwStringId,
286 __in_z LPCWSTR szData
287 )
288{
289 HRESULT hr = S_OK;
290 LPWSTR pwzData = NULL;
291 size_t cchData = 0;
292
293 hr = ::StringCchLengthW(szData, STRSAFE_MAX_LENGTH, &cchData);
294 ReswExitOnRootFailure(hr, "Failed to get block string length.");
295
296 pwzData = static_cast<LPWSTR>(MemAlloc((cchData + 1) * sizeof(WCHAR), TRUE));
297 ReswExitOnNull(pwzData, hr, E_OUTOFMEMORY, "Failed to allocate new block string.");
298
299 hr = ::StringCchCopyW(pwzData, cchData + 1, szData);
300 ReswExitOnRootFailure(hr, "Failed to copy new block string.");
301
302 ReleaseNullMem(pStrBlock->rgwz[dwStringId]);
303
304 pStrBlock->rgwz[dwStringId] = pwzData;
305 pwzData = NULL;
306
307LExit:
308 ReleaseMem(pwzData);
309
310 return hr;
311}
312
313
314static HRESULT StringBlockConvertToResourceData(
315 __in const RES_STRING_BLOCK* pStrBlock,
316 __deref_out_bcount(*pcbData) LPVOID* ppvData,
317 __out DWORD* pcbData
318 )
319{
320 HRESULT hr = S_OK;
321 DWORD cbData = 0;
322 LPVOID pvData = NULL;
323 WCHAR* pwz = NULL;
324
325 for (DWORD i = 0; i < RES_STRINGS_PER_BLOCK; ++i)
326 {
327 cbData += (lstrlenW(pStrBlock->rgwz[i]) + 1);
328 }
329 cbData *= sizeof(WCHAR);
330
331 pvData = MemAlloc(cbData, TRUE);
332 ReswExitOnNull(pvData, hr, E_OUTOFMEMORY, "Failed to allocate buffer to convert string block.");
333
334 pwz = static_cast<LPWSTR>(pvData);
335 for (DWORD i = 0; i < RES_STRINGS_PER_BLOCK; ++i)
336 {
337 DWORD cch = lstrlenW(pStrBlock->rgwz[i]);
338
339 *pwz = static_cast<WCHAR>(cch);
340 ++pwz;
341
342 for (DWORD j = 0; j < cch; ++j)
343 {
344 *pwz = pStrBlock->rgwz[i][j];
345 ++pwz;
346 }
347 }
348
349 *pcbData = cbData;
350 *ppvData = pvData;
351 pvData = NULL;
352
353LExit:
354 ReleaseMem(pvData);
355
356 return hr;
357}
358
359
360static HRESULT StringBlockConvertFromResourceData(
361 __in RES_STRING_BLOCK* pStrBlock,
362 __in_bcount(cbData) LPCVOID pvData,
363 __in SIZE_T cbData
364 )
365{
366 UNREFERENCED_PARAMETER(cbData);
367 HRESULT hr = S_OK;
368 LPCWSTR pwzParse = static_cast<LPCWSTR>(pvData);
369
370 for (DWORD i = 0; i < RES_STRINGS_PER_BLOCK; ++i)
371 {
372 DWORD cchParse = static_cast<DWORD>(*pwzParse);
373 ++pwzParse;
374
375 pStrBlock->rgwz[i] = static_cast<LPWSTR>(MemAlloc((cchParse + 1) * sizeof(WCHAR), TRUE));
376 ReswExitOnNull(pStrBlock->rgwz[i], hr, E_OUTOFMEMORY, "Failed to populate pStrBlock.");
377
378 hr = ::StringCchCopyNExW(pStrBlock->rgwz[i], cchParse + 1, pwzParse, cchParse, NULL, NULL, STRSAFE_FILL_BEHIND_NULL);
379 ReswExitOnFailure(hr, "Failed to copy parsed resource data into string block.");
380
381 pwzParse += cchParse;
382 }
383
384LExit:
385 return hr;
386}
diff --git a/src/libs/dutil/WixToolset.DUtil/rexutil.cpp b/src/libs/dutil/WixToolset.DUtil/rexutil.cpp
new file mode 100644
index 00000000..155ca714
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/rexutil.cpp
@@ -0,0 +1,601 @@
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 "rexutil.h"
5
6
7// Exit macros
8#define RexExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_REXUTIL, x, s, __VA_ARGS__)
9#define RexExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_REXUTIL, x, s, __VA_ARGS__)
10#define RexExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_REXUTIL, x, s, __VA_ARGS__)
11#define RexExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_REXUTIL, x, s, __VA_ARGS__)
12#define RexExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_REXUTIL, x, s, __VA_ARGS__)
13#define RexExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_REXUTIL, x, s, __VA_ARGS__)
14#define RexExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_REXUTIL, p, x, e, s, __VA_ARGS__)
15#define RexExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_REXUTIL, p, x, s, __VA_ARGS__)
16#define RexExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_REXUTIL, p, x, e, s, __VA_ARGS__)
17#define RexExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_REXUTIL, p, x, s, __VA_ARGS__)
18#define RexExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_REXUTIL, e, x, s, __VA_ARGS__)
19#define RexExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_REXUTIL, g, x, s, __VA_ARGS__)
20
21//
22// static globals
23//
24static HMODULE vhCabinetDll = NULL;
25static HFDI vhfdi = NULL;
26static ERF verf;
27
28static FAKE_FILE vrgffFileTable[FILETABLESIZE];
29static DWORD vcbRes;
30static LPCBYTE vpbRes;
31static CHAR vszResource[MAX_PATH];
32static REX_CALLBACK_WRITE vpfnWrite = NULL;
33
34static HRESULT vhrLastError = S_OK;
35
36//
37// structs
38//
39struct REX_CALLBACK_STRUCT
40{
41 BOOL fStopExtracting; // flag set when no more files are needed
42 LPCWSTR pwzExtract; // file to extract ("*" means extract all)
43 LPCWSTR pwzExtractDir; // directory to extract files to
44 LPCWSTR pwzExtractName; // name of file (pwzExtract can't be "*")
45
46 // possible user data
47 REX_CALLBACK_PROGRESS pfnProgress;
48 LPVOID pvContext;
49};
50
51//
52// prototypes
53//
54static __callback LPVOID DIAMONDAPI RexAlloc(DWORD dwSize);
55static __callback void DIAMONDAPI RexFree(LPVOID pvData);
56static __callback INT_PTR FAR DIAMONDAPI RexOpen(__in_z char FAR *pszFile, int oflag, int pmode);
57static __callback UINT FAR DIAMONDAPI RexRead(INT_PTR hf, __out_bcount(cb) void FAR *pv, UINT cb);
58static __callback UINT FAR DIAMONDAPI RexWrite(INT_PTR hf, __in_bcount(cb) void FAR *pv, UINT cb);
59static __callback int FAR DIAMONDAPI RexClose(INT_PTR hf);
60static __callback long FAR DIAMONDAPI RexSeek(INT_PTR hf, long dist, int seektype);
61static __callback INT_PTR DIAMONDAPI RexCallback(FDINOTIFICATIONTYPE iNotification, FDINOTIFICATION *pFDINotify);
62
63
64/********************************************************************
65 RexInitialize - initializes internal static variables
66
67*******************************************************************/
68extern "C" HRESULT RexInitialize()
69{
70 Assert(!vhfdi);
71
72 HRESULT hr = S_OK;
73
74 vhfdi = ::FDICreate(RexAlloc, RexFree, RexOpen, RexRead, RexWrite, RexClose, RexSeek, cpuUNKNOWN, &verf);
75 if (!vhfdi)
76 {
77 hr = E_FAIL;
78 RexExitOnFailure(hr, "failed to initialize cabinet.dll"); // TODO: put verf info in trace message here
79 }
80
81 ::ZeroMemory(vrgffFileTable, sizeof(vrgffFileTable));
82
83LExit:
84 if (FAILED(hr))
85 {
86 ::FDIDestroy(vhfdi);
87 vhfdi = NULL;
88 }
89
90 return hr;
91}
92
93
94/********************************************************************
95 RexUninitialize - initializes internal static variables
96
97*******************************************************************/
98extern "C" void RexUninitialize()
99{
100 if (vhfdi)
101 {
102 ::FDIDestroy(vhfdi);
103 vhfdi = NULL;
104 }
105}
106
107
108/********************************************************************
109 RexExtract - extracts one or all files from a resource cabinet
110
111 NOTE: wzExtractId can be a single file id or "*" to extract all files
112 wzExttractDir must be normalized (end in a "\")
113 wzExtractName is ignored if wzExtractId is "*"
114*******************************************************************/
115extern "C" HRESULT RexExtract(
116 __in_z LPCSTR szResource,
117 __in_z LPCWSTR wzExtractId,
118 __in_z LPCWSTR wzExtractDir,
119 __in_z LPCWSTR wzExtractName,
120 __in REX_CALLBACK_PROGRESS pfnProgress,
121 __in REX_CALLBACK_WRITE pfnWrite,
122 __in LPVOID pvContext
123 )
124{
125 Assert(vhfdi);
126 HRESULT hr = S_OK;
127 BOOL fResult;
128
129 HRSRC hResInfo = NULL;
130 HANDLE hRes = NULL;
131
132 REX_CALLBACK_STRUCT rcs;
133
134 // remember the write callback
135 vpfnWrite = pfnWrite;
136
137 //
138 // load the cabinet resource
139 //
140 hResInfo = ::FindResourceExA(NULL, RT_RCDATA, szResource, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL));
141 RexExitOnNullWithLastError(hResInfo, hr, "Failed to find resource.");
142 //hResInfo = ::FindResourceW(NULL, wzResource, /*RT_RCDATA*/MAKEINTRESOURCEW(10));
143 //ExitOnNullWithLastError(hResInfo, hr, "failed to load resource info");
144
145 hRes = ::LoadResource(NULL, hResInfo);
146 RexExitOnNullWithLastError(hRes, hr, "failed to load resource");
147
148 vcbRes = ::SizeofResource(NULL, hResInfo);
149 vpbRes = (const BYTE*)::LockResource(hRes);
150
151 // TODO: Call FDIIsCabinet to confirm resource is a cabinet before trying to extract from it
152
153 //
154 // convert the resource name to multi-byte
155 //
156 //if (!::WideCharToMultiByte(CP_ACP, 0, wzResource, -1, vszResource, countof(vszResource), NULL, NULL))
157 //{
158 // RexExitOnLastError(hr, "failed to convert cabinet resource name to ASCII: %ls", wzResource);
159 //}
160
161 hr = ::StringCchCopyA(vszResource, countof(vszResource), szResource);
162 RexExitOnFailure(hr, "Failed to copy resource name to global.");
163
164 //
165 // iterate through files in cabinet extracting them to the callback function
166 //
167 rcs.fStopExtracting = FALSE;
168 rcs.pwzExtract = wzExtractId;
169 rcs.pwzExtractDir = wzExtractDir;
170 rcs.pwzExtractName = wzExtractName;
171 rcs.pfnProgress = pfnProgress;
172 rcs.pvContext = pvContext;
173
174 fResult = ::FDICopy(vhfdi, vszResource, "", 0, RexCallback, NULL, static_cast<void*>(&rcs));
175 if (!fResult && !rcs.fStopExtracting) // if something went wrong and it wasn't us just stopping the extraction, then return a failure
176 {
177 hr = vhrLastError; // TODO: put verf info in trace message here
178 }
179
180LExit:
181 return hr;
182}
183
184
185/****************************************************************************
186 default extract routines
187
188****************************************************************************/
189static __callback LPVOID DIAMONDAPI RexAlloc(DWORD dwSize)
190{
191 return MemAlloc(dwSize, FALSE);
192}
193
194
195static __callback void DIAMONDAPI RexFree(LPVOID pvData)
196{
197 MemFree(pvData);
198}
199
200
201static __callback INT_PTR FAR DIAMONDAPI RexOpen(__in_z char FAR *pszFile, int oflag, int pmode)
202{
203 HRESULT hr = S_OK;
204 HANDLE hFile = INVALID_HANDLE_VALUE;
205 int i = 0;
206
207 // if FDI asks for some unusual mode (__in low memory situation it could ask for a scratch file) fail
208 if ((oflag != (/*_O_BINARY*/ 0x8000 | /*_O_RDONLY*/ 0x0000)) || (pmode != (_S_IREAD | _S_IWRITE)))
209 {
210 hr = E_OUTOFMEMORY;
211 RexExitOnFailure(hr, "FDI asked for to create a scratch file, which is unusual");
212 }
213
214 // find an empty spot in the fake file table
215 for (i = 0; i < FILETABLESIZE; ++i)
216 {
217 if (!vrgffFileTable[i].fUsed)
218 {
219 break;
220 }
221 }
222
223 // we should never run out of space in the fake file table
224 if (FILETABLESIZE <= i)
225 {
226 hr = E_OUTOFMEMORY;
227 RexExitOnFailure(hr, "File table exceeded");
228 }
229
230 if (0 == lstrcmpA(vszResource, pszFile))
231 {
232 vrgffFileTable[i].fUsed = TRUE;
233 vrgffFileTable[i].fftType = MEMORY_FILE;
234 vrgffFileTable[i].mfFile.vpStart = static_cast<LPCBYTE>(vpbRes);
235 vrgffFileTable[i].mfFile.uiCurrent = 0;
236 vrgffFileTable[i].mfFile.uiLength = vcbRes;
237 }
238 else // it's a real file
239 {
240 hFile = ::CreateFileA(pszFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
241 if (INVALID_HANDLE_VALUE == hFile)
242 {
243 RexExitWithLastError(hr, "failed to open file: %s", pszFile);
244 }
245
246 vrgffFileTable[i].fUsed = TRUE;
247 vrgffFileTable[i].fftType = NORMAL_FILE;
248 vrgffFileTable[i].hFile = hFile;
249 }
250
251LExit:
252 if (FAILED(hr))
253 {
254 vhrLastError = hr;
255 }
256
257 return FAILED(hr) ? -1 : i;
258}
259
260
261static __callback UINT FAR DIAMONDAPI RexRead(INT_PTR hf, __out_bcount(cb) void FAR *pv, UINT cb)
262{
263 Assert(vrgffFileTable[hf].fUsed);
264
265 HRESULT hr = S_OK;
266 DWORD cbRead = 0;
267 DWORD cbAvailable = 0;
268
269 if (MEMORY_FILE == vrgffFileTable[hf].fftType)
270 {
271 // ensure that we don't read past the length of the resource
272 cbAvailable = vrgffFileTable[hf].mfFile.uiLength - vrgffFileTable[hf].mfFile.uiCurrent;
273 cbRead = cb < cbAvailable? cb : cbAvailable;
274
275 memcpy(pv, static_cast<const void *>(vrgffFileTable[hf].mfFile.vpStart + vrgffFileTable[hf].mfFile.uiCurrent), cbRead);
276
277 vrgffFileTable[hf].mfFile.uiCurrent += cbRead;
278 }
279 else // NORMAL_FILE
280 {
281 Assert(vrgffFileTable[hf].hFile && vrgffFileTable[hf].hFile != INVALID_HANDLE_VALUE);
282
283 if (!::ReadFile(vrgffFileTable[hf].hFile, pv, cb, &cbRead, NULL))
284 {
285 RexExitWithLastError(hr, "failed to read during cabinet extraction");
286 }
287 }
288
289LExit:
290 if (FAILED(hr))
291 {
292 vhrLastError = hr;
293 }
294
295 return FAILED(hr) ? -1 : cbRead;
296}
297
298
299static __callback UINT FAR DIAMONDAPI RexWrite(INT_PTR hf, __in_bcount(cb) void FAR *pv, UINT cb)
300{
301 Assert(vrgffFileTable[hf].fUsed);
302 Assert(vrgffFileTable[hf].fftType == NORMAL_FILE); // we should never be writing to a memory file
303
304 HRESULT hr = S_OK;
305 DWORD cbWrite = 0;
306
307 Assert(vrgffFileTable[hf].hFile && vrgffFileTable[hf].hFile != INVALID_HANDLE_VALUE);
308 if (!::WriteFile(reinterpret_cast<HANDLE>(vrgffFileTable[hf].hFile), pv, cb, &cbWrite, NULL))
309 {
310 RexExitWithLastError(hr, "failed to write during cabinet extraction");
311 }
312
313 // call the writer callback if defined
314 if (vpfnWrite)
315 {
316 vpfnWrite(cb);
317 }
318
319LExit:
320 if (FAILED(hr))
321 {
322 vhrLastError = hr;
323 }
324
325 return FAILED(hr) ? -1 : cbWrite;
326}
327
328
329static __callback long FAR DIAMONDAPI RexSeek(INT_PTR hf, long dist, int seektype)
330{
331 Assert(vrgffFileTable[hf].fUsed);
332
333 HRESULT hr = S_OK;
334 DWORD dwMoveMethod;
335 LONG lMove = 0;
336
337 switch (seektype)
338 {
339 case 0: // SEEK_SET
340 dwMoveMethod = FILE_BEGIN;
341 break;
342 case 1: /// SEEK_CUR
343 dwMoveMethod = FILE_CURRENT;
344 break;
345 case 2: // SEEK_END
346 dwMoveMethod = FILE_END;
347 break;
348 default :
349 dwMoveMethod = 0;
350 hr = E_UNEXPECTED;
351 RexExitOnFailure(hr, "unexpected seektype in FDISeek(): %d", seektype);
352 }
353
354 if (MEMORY_FILE == vrgffFileTable[hf].fftType)
355 {
356 if (FILE_BEGIN == dwMoveMethod)
357 {
358 vrgffFileTable[hf].mfFile.uiCurrent = dist;
359 }
360 else if (FILE_CURRENT == dwMoveMethod)
361 {
362 vrgffFileTable[hf].mfFile.uiCurrent += dist;
363 }
364 else // FILE_END
365 {
366 vrgffFileTable[hf].mfFile.uiCurrent = vrgffFileTable[hf].mfFile.uiLength + dist;
367 }
368
369 lMove = vrgffFileTable[hf].mfFile.uiCurrent;
370 }
371 else // NORMAL_FILE
372 {
373 Assert(vrgffFileTable[hf].hFile && vrgffFileTable[hf].hFile != INVALID_HANDLE_VALUE);
374
375 // SetFilePointer returns -1 if it fails (this will cause FDI to quit with an FDIERROR_USER_ABORT error.
376 // (Unless this happens while working on a cabinet, in which case FDI returns FDIERROR_CORRUPT_CABINET)
377 lMove = ::SetFilePointer(vrgffFileTable[hf].hFile, dist, NULL, dwMoveMethod);
378 if (0xFFFFFFFF == lMove)
379 {
380 RexExitWithLastError(hr, "failed to move file pointer %d bytes", dist);
381 }
382 }
383
384LExit:
385 if (FAILED(hr))
386 {
387 vhrLastError = hr;
388 }
389
390 return FAILED(hr) ? -1 : lMove;
391}
392
393
394__callback int FAR DIAMONDAPI RexClose(INT_PTR hf)
395{
396 Assert(vrgffFileTable[hf].fUsed);
397
398 HRESULT hr = S_OK;
399
400 if (MEMORY_FILE == vrgffFileTable[hf].fftType)
401 {
402 vrgffFileTable[hf].mfFile.vpStart = NULL;
403 vrgffFileTable[hf].mfFile.uiCurrent = 0;
404 vrgffFileTable[hf].mfFile.uiLength = 0;
405 }
406 else
407 {
408 Assert(vrgffFileTable[hf].hFile && vrgffFileTable[hf].hFile != INVALID_HANDLE_VALUE);
409
410 if (!::CloseHandle(vrgffFileTable[hf].hFile))
411 {
412 RexExitWithLastError(hr, "failed to close file during cabinet extraction");
413 }
414
415 vrgffFileTable[hf].hFile = INVALID_HANDLE_VALUE;
416 }
417
418 vrgffFileTable[hf].fUsed = FALSE;
419
420LExit:
421 if (FAILED(hr))
422 {
423 vhrLastError = hr;
424 }
425
426 return FAILED(hr) ? -1 : 0;
427}
428
429
430static __callback INT_PTR DIAMONDAPI RexCallback(FDINOTIFICATIONTYPE iNotification, FDINOTIFICATION *pFDINotify)
431{
432 Assert(pFDINotify->pv);
433
434 HRESULT hr = S_OK;
435 int ipResult = 0; // result to return on success
436 HANDLE hFile = INVALID_HANDLE_VALUE;
437
438 REX_CALLBACK_STRUCT* prcs = static_cast<REX_CALLBACK_STRUCT*>(pFDINotify->pv);
439 LPCSTR sz;
440 WCHAR wz[MAX_PATH];
441 FILETIME ft;
442 int i = 0;
443
444 switch (iNotification)
445 {
446 case fdintCOPY_FILE: // beGIN extracting a resource from cabinet
447 Assert(pFDINotify->psz1);
448
449 if (prcs->fStopExtracting)
450 {
451 ExitFunction1(hr = S_FALSE); // no more extracting
452 }
453
454 // convert params to useful variables
455 sz = static_cast<LPCSTR>(pFDINotify->psz1);
456 if (!::MultiByteToWideChar(CP_ACP, 0, sz, -1, wz, countof(wz)))
457 {
458 RexExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz);
459 }
460
461 if (prcs->pfnProgress)
462 {
463 hr = prcs->pfnProgress(TRUE, wz, prcs->pvContext);
464 if (S_OK != hr)
465 {
466 ExitFunction();
467 }
468 }
469
470 if (L'*' == *prcs->pwzExtract || 0 == lstrcmpW(prcs->pwzExtract, wz))
471 {
472 // get the created date for the resource in the cabinet
473 if (!::DosDateTimeToFileTime(pFDINotify->date, pFDINotify->time, &ft))
474 {
475 RexExitWithLastError(hr, "failed to get time for resource: %ls", wz);
476 }
477
478 WCHAR wzPath[MAX_PATH];
479
480 hr = ::StringCchCopyW(wzPath, countof(wzPath), prcs->pwzExtractDir);
481 RexExitOnFailure(hr, "failed to copy extract directory: %ls for file: %ls", prcs->pwzExtractDir, wz);
482
483 if (L'*' == *prcs->pwzExtract)
484 {
485 hr = ::StringCchCatW(wzPath, countof(wzPath), wz);
486 RexExitOnFailure(hr, "failed to concat onto path: %ls file: %ls", wzPath, wz);
487 }
488 else
489 {
490 Assert(*prcs->pwzExtractName);
491
492 hr = ::StringCchCatW(wzPath, countof(wzPath), prcs->pwzExtractName);
493 RexExitOnFailure(hr, "failed to concat onto path: %ls file: %ls", wzPath, prcs->pwzExtractName);
494 }
495
496 // Quickly chop off the file name part of the path to ensure the path exists
497 // then put the file name back on the path (by putting the first character
498 // back over the null terminator).
499 LPWSTR wzFile = PathFile(wzPath);
500 WCHAR wzFileFirstChar = *wzFile;
501 *wzFile = L'\0';
502
503 hr = DirEnsureExists(wzPath, NULL);
504 RexExitOnFailure(hr, "failed to ensure directory: %ls", wzPath);
505
506 hr = S_OK;
507
508 *wzFile = wzFileFirstChar;
509
510 // find an empty spot in the fake file table
511 for (i = 0; i < FILETABLESIZE; ++i)
512 {
513 if (!vrgffFileTable[i].fUsed)
514 {
515 break;
516 }
517 }
518
519 // we should never run out of space in the fake file table
520 if (FILETABLESIZE <= i)
521 {
522 hr = E_OUTOFMEMORY;
523 RexExitOnFailure(hr, "File table exceeded");
524 }
525
526 // open the file
527 hFile = ::CreateFileW(wzPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
528 if (INVALID_HANDLE_VALUE == hFile)
529 {
530 RexExitWithLastError(hr, "failed to open file: %ls", wzPath);
531 }
532
533 vrgffFileTable[i].fUsed = TRUE;
534 vrgffFileTable[i].fftType = NORMAL_FILE;
535 vrgffFileTable[i].hFile = hFile;
536
537 ipResult = i;
538
539 ::SetFileTime(vrgffFileTable[i].hFile, &ft, &ft, &ft); // try to set the file time (who cares if it fails)
540
541 if (::SetFilePointer(vrgffFileTable[i].hFile, pFDINotify->cb, NULL, FILE_BEGIN)) // try to set the end of the file (don't worry if this fails)
542 {
543 if (::SetEndOfFile(vrgffFileTable[i].hFile))
544 {
545 ::SetFilePointer(vrgffFileTable[i].hFile, 0, NULL, FILE_BEGIN); // reset the file pointer
546 }
547 }
548 }
549 else // resource wasn't requested, skip it
550 {
551 hr = S_OK;
552 ipResult = 0;
553 }
554
555 break;
556 case fdintCLOSE_FILE_INFO: // resource extraction complete
557 Assert(pFDINotify->hf && pFDINotify->psz1);
558
559 // convert params to useful variables
560 sz = static_cast<LPCSTR>(pFDINotify->psz1);
561 if (!::MultiByteToWideChar(CP_ACP, 0, sz, -1, wz, countof(wz)))
562 {
563 RexExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz);
564 }
565
566 RexClose(pFDINotify->hf);
567
568 if (prcs->pfnProgress)
569 {
570 hr = prcs->pfnProgress(FALSE, wz, prcs->pvContext);
571 }
572
573 if (S_OK == hr && L'*' == *prcs->pwzExtract) // if everything is okay and we're extracting all files, keep going
574 {
575 ipResult = TRUE;
576 }
577 else // something went wrong or we only needed to extract one file
578 {
579 hr = S_OK;
580 ipResult = FALSE;
581 prcs->fStopExtracting = TRUE;
582 }
583
584 break;
585 case fdintPARTIAL_FILE: __fallthrough; // no action needed for these messages, fall through
586 case fdintNEXT_CABINET: __fallthrough;
587 case fdintENUMERATE: __fallthrough;
588 case fdintCABINET_INFO:
589 break;
590 default:
591 AssertSz(FALSE, "RexCallback() - unknown FDI notification command");
592 };
593
594LExit:
595 if (FAILED(hr))
596 {
597 vhrLastError = hr;
598 }
599
600 return (S_OK == hr) ? ipResult : -1;
601}
diff --git a/src/libs/dutil/WixToolset.DUtil/rmutil.cpp b/src/libs/dutil/WixToolset.DUtil/rmutil.cpp
new file mode 100644
index 00000000..95c8c8a4
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/rmutil.cpp
@@ -0,0 +1,488 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4#include <restartmanager.h>
5
6
7// Exit macros
8#define RmExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_RMUTIL, x, s, __VA_ARGS__)
9#define RmExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_RMUTIL, x, s, __VA_ARGS__)
10#define RmExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_RMUTIL, x, s, __VA_ARGS__)
11#define RmExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_RMUTIL, x, s, __VA_ARGS__)
12#define RmExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_RMUTIL, x, s, __VA_ARGS__)
13#define RmExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_RMUTIL, x, s, __VA_ARGS__)
14#define RmExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_RMUTIL, p, x, e, s, __VA_ARGS__)
15#define RmExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_RMUTIL, p, x, s, __VA_ARGS__)
16#define RmExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_RMUTIL, p, x, e, s, __VA_ARGS__)
17#define RmExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_RMUTIL, p, x, s, __VA_ARGS__)
18#define RmExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_RMUTIL, e, x, s, __VA_ARGS__)
19#define RmExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_RMUTIL, g, x, s, __VA_ARGS__)
20
21#define ARRAY_GROWTH_SIZE 5
22
23typedef DWORD (WINAPI *PFNRMJOINSESSION)(
24 __out DWORD *pSessionHandle,
25 __in_z const WCHAR strSessionKey[]
26 );
27
28typedef DWORD (WINAPI *PFNRMENDSESSION)(
29 __in DWORD dwSessionHandle
30 );
31
32typedef DWORD (WINAPI *PFNRMREGISTERRESOURCES)(
33 __in DWORD dwSessionHandle,
34 __in UINT nFiles,
35 __in_z_opt LPWSTR *rgsFilenames,
36 __in UINT nApplications,
37 __in_opt RM_UNIQUE_PROCESS *rgApplications,
38 __in UINT nServices,
39 __in_z_opt LPWSTR *rgsServiceNames
40 );
41
42typedef struct _RMU_SESSION
43{
44 CRITICAL_SECTION cs;
45 DWORD dwSessionHandle;
46 BOOL fStartedSessionHandle;
47 BOOL fInitialized;
48
49 UINT cFilenames;
50 LPWSTR *rgsczFilenames;
51
52 UINT cApplications;
53 RM_UNIQUE_PROCESS *rgApplications;
54
55 UINT cServiceNames;
56 LPWSTR *rgsczServiceNames;
57
58} RMU_SESSION;
59
60static volatile LONG vcRmuInitialized = 0;
61static HMODULE vhModule = NULL;
62static PFNRMJOINSESSION vpfnRmJoinSession = NULL;
63static PFNRMENDSESSION vpfnRmEndSession = NULL;
64static PFNRMREGISTERRESOURCES vpfnRmRegisterResources = NULL;
65
66static HRESULT RmuInitialize();
67static void RmuUninitialize();
68
69static HRESULT RmuApplicationArrayAlloc(
70 __deref_inout_ecount(*pcApplications) RM_UNIQUE_PROCESS **prgApplications,
71 __inout LPUINT pcApplications,
72 __in DWORD dwProcessId,
73 __in FILETIME ProcessStartTime
74 );
75
76static HRESULT RmuApplicationArrayFree(
77 __in RM_UNIQUE_PROCESS *rgApplications
78 );
79
80#define ReleaseNullApplicationArray(rg, c) { if (rg) { RmuApplicationArrayFree(rg); c = 0; rg = NULL; } }
81
82/********************************************************************
83RmuJoinSession - Joins an existing Restart Manager session.
84
85********************************************************************/
86extern "C" HRESULT DAPI RmuJoinSession(
87 __out PRMU_SESSION *ppSession,
88 __in_z LPCWSTR wzSessionKey
89 )
90{
91 HRESULT hr = S_OK;
92 DWORD er = ERROR_SUCCESS;
93 PRMU_SESSION pSession = NULL;
94
95 *ppSession = NULL;
96
97 pSession = static_cast<PRMU_SESSION>(MemAlloc(sizeof(RMU_SESSION), TRUE));
98 RmExitOnNull(pSession, hr, E_OUTOFMEMORY, "Failed to allocate the RMU_SESSION structure.");
99
100 hr = RmuInitialize();
101 RmExitOnFailure(hr, "Failed to initialize Restart Manager.");
102
103 er = vpfnRmJoinSession(&pSession->dwSessionHandle, wzSessionKey);
104 RmExitOnWin32Error(er, hr, "Failed to join Restart Manager session %ls.", wzSessionKey);
105
106 ::InitializeCriticalSection(&pSession->cs);
107 pSession->fInitialized = TRUE;
108
109 *ppSession = pSession;
110
111LExit:
112 if (FAILED(hr))
113 {
114 ReleaseNullMem(pSession);
115 }
116
117 return hr;
118}
119
120/********************************************************************
121RmuAddFile - Adds the file path to the Restart Manager session.
122
123You should call this multiple times as necessary before calling
124RmuRegisterResources.
125
126********************************************************************/
127extern "C" HRESULT DAPI RmuAddFile(
128 __in PRMU_SESSION pSession,
129 __in_z LPCWSTR wzPath
130 )
131{
132 HRESULT hr = S_OK;
133
134 ::EnterCriticalSection(&pSession->cs);
135
136 // Create or grow the jagged array.
137 hr = StrArrayAllocString(&pSession->rgsczFilenames, &pSession->cFilenames, wzPath, 0);
138 RmExitOnFailure(hr, "Failed to add the filename to the array.");
139
140LExit:
141 ::LeaveCriticalSection(&pSession->cs);
142 return hr;
143}
144
145/********************************************************************
146RmuAddProcessById - Adds the process ID to the Restart Manager sesion.
147
148You should call this multiple times as necessary before calling
149RmuRegisterResources.
150
151********************************************************************/
152extern "C" HRESULT DAPI RmuAddProcessById(
153 __in PRMU_SESSION pSession,
154 __in DWORD dwProcessId
155 )
156{
157 HRESULT hr = S_OK;
158 HANDLE hProcess = NULL;
159 FILETIME CreationTime = {};
160 FILETIME ExitTime = {};
161 FILETIME KernelTime = {};
162 FILETIME UserTime = {};
163 BOOL fLocked = FALSE;
164
165 HANDLE hToken = NULL;
166 TOKEN_PRIVILEGES priv = { 0 };
167 TOKEN_PRIVILEGES* pPrevPriv = NULL;
168 DWORD cbPrevPriv = 0;
169 DWORD er = ERROR_SUCCESS;
170 BOOL fAdjustedPrivileges = FALSE;
171 BOOL fElevated = FALSE;
172 ProcElevated(::GetCurrentProcess(), &fElevated);
173
174 // Must be elevated to adjust process privileges
175 if (fElevated) {
176 // Adding SeDebugPrivilege in the event that the process targeted by ::OpenProcess() is in a another user context.
177 if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &hToken))
178 {
179 RmExitWithLastError(hr, "Failed to get process token.");
180 }
181
182 priv.PrivilegeCount = 1;
183 priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
184 if (!::LookupPrivilegeValueW(NULL, L"SeDebugPrivilege", &priv.Privileges[0].Luid))
185 {
186 RmExitWithLastError(hr, "Failed to get debug privilege LUID.");
187 }
188
189 cbPrevPriv = sizeof(TOKEN_PRIVILEGES);
190 pPrevPriv = static_cast<TOKEN_PRIVILEGES*>(MemAlloc(cbPrevPriv, TRUE));
191 RmExitOnNull(pPrevPriv, hr, E_OUTOFMEMORY, "Failed to allocate memory for empty previous privileges.");
192
193 if (!::AdjustTokenPrivileges(hToken, FALSE, &priv, cbPrevPriv, pPrevPriv, &cbPrevPriv))
194 {
195 LPVOID pv = MemReAlloc(pPrevPriv, cbPrevPriv, TRUE);
196 RmExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for previous privileges.");
197 pPrevPriv = static_cast<TOKEN_PRIVILEGES*>(pv);
198
199 if (!::AdjustTokenPrivileges(hToken, FALSE, &priv, cbPrevPriv, pPrevPriv, &cbPrevPriv))
200 {
201 RmExitWithLastError(hr, "Failed to get debug privilege LUID.");
202 }
203 }
204
205 fAdjustedPrivileges = TRUE;
206 }
207
208 hProcess = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, dwProcessId);
209 if (hProcess)
210 {
211 if (!::GetProcessTimes(hProcess, &CreationTime, &ExitTime, &KernelTime, &UserTime))
212 {
213 RmExitWithLastError(hr, "Failed to get the process times for process ID %d.", dwProcessId);
214 }
215
216 ::EnterCriticalSection(&pSession->cs);
217 fLocked = TRUE;
218 hr = RmuApplicationArrayAlloc(&pSession->rgApplications, &pSession->cApplications, dwProcessId, CreationTime);
219 RmExitOnFailure(hr, "Failed to add the application to the array.");
220 }
221 else
222 {
223 er = ::GetLastError();
224 if (ERROR_ACCESS_DENIED == er)
225 {
226 // OpenProcess will fail when not elevated and the target process is in another user context. Let the caller log and continue.
227 hr = E_NOTFOUND;
228 }
229 else
230 {
231 RmExitOnWin32Error(er, hr, "Failed to open the process ID %d.", dwProcessId);
232 }
233 }
234
235LExit:
236 if (hProcess)
237 {
238 ::CloseHandle(hProcess);
239 }
240
241 if (fAdjustedPrivileges)
242 {
243 ::AdjustTokenPrivileges(hToken, FALSE, pPrevPriv, 0, NULL, NULL);
244 }
245
246 ReleaseMem(pPrevPriv);
247 ReleaseHandle(hToken);
248
249 if (fLocked)
250 {
251 ::LeaveCriticalSection(&pSession->cs);
252 }
253
254 return hr;
255}
256
257/********************************************************************
258RmuAddProcessesByName - Adds all processes by the given process name
259 to the Restart Manager Session.
260
261You should call this multiple times as necessary before calling
262RmuRegisterResources.
263
264********************************************************************/
265extern "C" HRESULT DAPI RmuAddProcessesByName(
266 __in PRMU_SESSION pSession,
267 __in_z LPCWSTR wzProcessName
268 )
269{
270 HRESULT hr = S_OK;
271 DWORD *pdwProcessIds = NULL;
272 DWORD cProcessIds = 0;
273 BOOL fNotFound = FALSE;
274
275 hr = ProcFindAllIdsFromExeName(wzProcessName, &pdwProcessIds, &cProcessIds);
276 RmExitOnFailure(hr, "Failed to enumerate all the processes by name %ls.", wzProcessName);
277
278 for (DWORD i = 0; i < cProcessIds; ++i)
279 {
280 hr = RmuAddProcessById(pSession, pdwProcessIds[i]);
281 if (E_NOTFOUND == hr)
282 {
283 // RmuAddProcessById returns E_NOTFOUND when this setup is not elevated and OpenProcess returned access denied (target process running under another user account).
284 fNotFound = TRUE;
285 }
286 else
287 {
288 RmExitOnFailure(hr, "Failed to add process %ls (%d) to the Restart Manager session.", wzProcessName, pdwProcessIds[i]);
289 }
290 }
291
292 // If one or more calls to RmuAddProcessById returned E_NOTFOUND, then return E_NOTFOUND even if other calls succeeded, so that caller can log the issue.
293 if (fNotFound)
294 {
295 hr = E_NOTFOUND;
296 }
297
298LExit:
299 ReleaseMem(pdwProcessIds);
300
301 return hr;
302}
303
304/********************************************************************
305RmuAddService - Adds the service name to the Restart Manager session.
306
307You should call this multiple times as necessary before calling
308RmuRegisterResources.
309
310********************************************************************/
311extern "C" HRESULT DAPI RmuAddService(
312 __in PRMU_SESSION pSession,
313 __in_z LPCWSTR wzServiceName
314 )
315{
316 HRESULT hr = S_OK;
317
318 ::EnterCriticalSection(&pSession->cs);
319
320 hr = StrArrayAllocString(&pSession->rgsczServiceNames, &pSession->cServiceNames, wzServiceName, 0);
321 RmExitOnFailure(hr, "Failed to add the service name to the array.");
322
323LExit:
324 ::LeaveCriticalSection(&pSession->cs);
325 return hr;
326}
327
328/********************************************************************
329RmuRegisterResources - Registers resources for the Restart Manager.
330
331This should be called rarely because it is expensive to run. Call
332functions like RmuAddFile for multiple resources then commit them
333as a batch of updates to RmuRegisterResources.
334
335Duplicate resources appear to be handled by Restart Manager.
336Only one WM_QUERYENDSESSION is being sent for each top-level window.
337
338********************************************************************/
339extern "C" HRESULT DAPI RmuRegisterResources(
340 __in PRMU_SESSION pSession
341 )
342{
343 HRESULT hr = S_OK;
344 DWORD er = ERROR_SUCCESS;
345
346 AssertSz(vcRmuInitialized, "Restart Manager was not properly initialized.");
347
348 ::EnterCriticalSection(&pSession->cs);
349
350 er = vpfnRmRegisterResources(
351 pSession->dwSessionHandle,
352 pSession->cFilenames,
353 pSession->rgsczFilenames,
354 pSession->cApplications,
355 pSession->rgApplications,
356 pSession->cServiceNames,
357 pSession->rgsczServiceNames
358 );
359 RmExitOnWin32Error(er, hr, "Failed to register the resources with the Restart Manager session.");
360
361 // Empty the arrays if registered in case additional resources are added later.
362 ReleaseNullStrArray(pSession->rgsczFilenames, pSession->cFilenames);
363 ReleaseNullApplicationArray(pSession->rgApplications, pSession->cApplications);
364 ReleaseNullStrArray(pSession->rgsczServiceNames, pSession->cServiceNames);
365
366LExit:
367 ::LeaveCriticalSection(&pSession->cs);
368 return hr;
369}
370
371/********************************************************************
372RmuEndSession - Ends the session.
373
374If the session was joined by RmuJoinSession, any remaining resources
375are registered before the session is ended (left).
376
377********************************************************************/
378extern "C" HRESULT DAPI RmuEndSession(
379 __in PRMU_SESSION pSession
380 )
381{
382 HRESULT hr = S_OK;
383 DWORD er = ERROR_SUCCESS;
384
385 AssertSz(vcRmuInitialized, "Restart Manager was not properly initialized.");
386
387 // Make sure all resources are registered if we joined the session.
388 if (!pSession->fStartedSessionHandle)
389 {
390 hr = RmuRegisterResources(pSession);
391 RmExitOnFailure(hr, "Failed to register remaining resources.");
392 }
393
394 er = vpfnRmEndSession(pSession->dwSessionHandle);
395 RmExitOnWin32Error(er, hr, "Failed to end the Restart Manager session.");
396
397LExit:
398 if (pSession->fInitialized)
399 {
400 ::DeleteCriticalSection(&pSession->cs);
401 }
402
403 ReleaseNullStrArray(pSession->rgsczFilenames, pSession->cFilenames);
404 ReleaseNullApplicationArray(pSession->rgApplications, pSession->cApplications);
405 ReleaseNullStrArray(pSession->rgsczServiceNames, pSession->cServiceNames);
406 ReleaseNullMem(pSession);
407
408 RmuUninitialize();
409
410 return hr;
411}
412
413static HRESULT RmuInitialize()
414{
415 HRESULT hr = S_OK;
416 HMODULE hModule = NULL;
417
418 LONG iRef = ::InterlockedIncrement(&vcRmuInitialized);
419 if (1 == iRef && !vhModule)
420 {
421 hr = LoadSystemLibrary(L"rstrtmgr.dll", &hModule);
422 RmExitOnFailure(hr, "Failed to load the rstrtmgr.dll module.");
423
424 vpfnRmJoinSession = reinterpret_cast<PFNRMJOINSESSION>(::GetProcAddress(hModule, "RmJoinSession"));
425 RmExitOnNullWithLastError(vpfnRmJoinSession, hr, "Failed to get the RmJoinSession procedure from rstrtmgr.dll.");
426
427 vpfnRmRegisterResources = reinterpret_cast<PFNRMREGISTERRESOURCES>(::GetProcAddress(hModule, "RmRegisterResources"));
428 RmExitOnNullWithLastError(vpfnRmRegisterResources, hr, "Failed to get the RmRegisterResources procedure from rstrtmgr.dll.");
429
430 vpfnRmEndSession = reinterpret_cast<PFNRMENDSESSION>(::GetProcAddress(hModule, "RmEndSession"));
431 RmExitOnNullWithLastError(vpfnRmEndSession, hr, "Failed to get the RmEndSession procedure from rstrtmgr.dll.");
432
433 vhModule = hModule;
434 }
435
436LExit:
437 return hr;
438}
439
440static void RmuUninitialize()
441{
442 LONG iRef = ::InterlockedDecrement(&vcRmuInitialized);
443 if (0 == iRef && vhModule)
444 {
445 vpfnRmJoinSession = NULL;
446 vpfnRmEndSession = NULL;
447 vpfnRmRegisterResources = NULL;
448
449 ::FreeLibrary(vhModule);
450 vhModule = NULL;
451 }
452}
453
454static HRESULT RmuApplicationArrayAlloc(
455 __deref_inout_ecount(*pcApplications) RM_UNIQUE_PROCESS **prgApplications,
456 __inout LPUINT pcApplications,
457 __in DWORD dwProcessId,
458 __in FILETIME ProcessStartTime
459 )
460{
461 HRESULT hr = S_OK;
462 RM_UNIQUE_PROCESS *pApplication = NULL;
463
464 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(prgApplications), *pcApplications + 1, sizeof(RM_UNIQUE_PROCESS), ARRAY_GROWTH_SIZE);
465 RmExitOnFailure(hr, "Failed to allocate memory for the application array.");
466
467 pApplication = static_cast<RM_UNIQUE_PROCESS*>(&(*prgApplications)[*pcApplications]);
468 pApplication->dwProcessId = dwProcessId;
469 pApplication->ProcessStartTime = ProcessStartTime;
470
471 ++(*pcApplications);
472
473LExit:
474 return hr;
475}
476
477static HRESULT RmuApplicationArrayFree(
478 __in RM_UNIQUE_PROCESS *rgApplications
479 )
480{
481 HRESULT hr = S_OK;
482
483 hr = MemFree(rgApplications);
484 RmExitOnFailure(hr, "Failed to free memory for the application array.");
485
486LExit:
487 return hr;
488}
diff --git a/src/libs/dutil/WixToolset.DUtil/rssutil.cpp b/src/libs/dutil/WixToolset.DUtil/rssutil.cpp
new file mode 100644
index 00000000..8f994dfc
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/rssutil.cpp
@@ -0,0 +1,647 @@
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// Exit macros
7#define RssExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_RSSUTIL, x, s, __VA_ARGS__)
8#define RssExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_RSSUTIL, x, s, __VA_ARGS__)
9#define RssExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_RSSUTIL, x, s, __VA_ARGS__)
10#define RssExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_RSSUTIL, x, s, __VA_ARGS__)
11#define RssExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_RSSUTIL, x, s, __VA_ARGS__)
12#define RssExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_RSSUTIL, x, s, __VA_ARGS__)
13#define RssExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_RSSUTIL, p, x, e, s, __VA_ARGS__)
14#define RssExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_RSSUTIL, p, x, s, __VA_ARGS__)
15#define RssExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_RSSUTIL, p, x, e, s, __VA_ARGS__)
16#define RssExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_RSSUTIL, p, x, s, __VA_ARGS__)
17#define RssExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_RSSUTIL, e, x, s, __VA_ARGS__)
18#define RssExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_RSSUTIL, g, x, s, __VA_ARGS__)
19
20static HRESULT ParseRssDocument(
21 __in IXMLDOMDocument *pixd,
22 __out RSS_CHANNEL **ppChannel
23 );
24static HRESULT ParseRssChannel(
25 __in IXMLDOMNode *pixnChannel,
26 __out RSS_CHANNEL **ppChannel
27 );
28static HRESULT ParseRssItem(
29 __in IXMLDOMNode *pixnItem,
30 __in DWORD cItem,
31 __in_xcount(pChannel->cItems) RSS_CHANNEL *pChannel
32 );
33static HRESULT ParseRssUnknownElement(
34 __in IXMLDOMNode *pNode,
35 __inout RSS_UNKNOWN_ELEMENT** ppUnknownElement
36 );
37static HRESULT ParseRssUnknownAttribute(
38 __in IXMLDOMNode *pNode,
39 __inout RSS_UNKNOWN_ATTRIBUTE** ppUnknownAttribute
40 );
41static void FreeRssUnknownElementList(
42 __in_opt RSS_UNKNOWN_ELEMENT* pUnknownElement
43 );
44static void FreeRssUnknownAttributeList(
45 __in_opt RSS_UNKNOWN_ATTRIBUTE* pUnknownAttribute
46 );
47
48
49/********************************************************************
50 RssInitialize - Initialize RSS utilities.
51
52*********************************************************************/
53extern "C" HRESULT DAPI RssInitialize()
54{
55 return XmlInitialize();
56}
57
58
59/********************************************************************
60 RssUninitialize - Uninitialize RSS utilities.
61
62*********************************************************************/
63extern "C" void DAPI RssUninitialize()
64{
65 XmlUninitialize();
66}
67
68
69/********************************************************************
70 RssParseFromString - parses out an RSS channel from a string.
71
72*********************************************************************/
73extern "C" HRESULT DAPI RssParseFromString(
74 __in_z LPCWSTR wzRssString,
75 __out RSS_CHANNEL **ppChannel
76 )
77{
78 Assert(wzRssString);
79 Assert(ppChannel);
80
81 HRESULT hr = S_OK;
82 RSS_CHANNEL *pNewChannel = NULL;
83 IXMLDOMDocument *pixdRss = NULL;
84
85 hr = XmlLoadDocument(wzRssString, &pixdRss);
86 RssExitOnFailure(hr, "Failed to load RSS string as XML document.");
87
88 hr = ParseRssDocument(pixdRss, &pNewChannel);
89 RssExitOnFailure(hr, "Failed to parse RSS document.");
90
91 *ppChannel = pNewChannel;
92 pNewChannel = NULL;
93
94LExit:
95 ReleaseObject(pixdRss);
96
97 ReleaseRssChannel(pNewChannel);
98
99 return hr;
100}
101
102
103/********************************************************************
104 RssParseFromFile - parses out an RSS channel from a file path.
105
106*********************************************************************/
107extern "C" HRESULT DAPI RssParseFromFile(
108 __in_z LPCWSTR wzRssFile,
109 __out RSS_CHANNEL **ppChannel
110 )
111{
112 Assert(wzRssFile);
113 Assert(ppChannel);
114
115 HRESULT hr = S_OK;
116 RSS_CHANNEL *pNewChannel = NULL;
117 IXMLDOMDocument *pixdRss = NULL;
118
119 hr = XmlLoadDocumentFromFile(wzRssFile, &pixdRss);
120 RssExitOnFailure(hr, "Failed to load RSS string as XML document.");
121
122 hr = ParseRssDocument(pixdRss, &pNewChannel);
123 RssExitOnFailure(hr, "Failed to parse RSS document.");
124
125 *ppChannel = pNewChannel;
126 pNewChannel = NULL;
127
128LExit:
129 ReleaseObject(pixdRss);
130
131 ReleaseRssChannel(pNewChannel);
132
133 return hr;
134}
135
136
137/********************************************************************
138 RssFreeChannel - parses out an RSS channel from a string.
139
140*********************************************************************/
141extern "C" void DAPI RssFreeChannel(
142 __in_xcount(pChannel->cItems) RSS_CHANNEL *pChannel
143 )
144{
145 if (pChannel)
146 {
147 for (DWORD i = 0; i < pChannel->cItems; ++i)
148 {
149 ReleaseStr(pChannel->rgItems[i].wzTitle);
150 ReleaseStr(pChannel->rgItems[i].wzLink);
151 ReleaseStr(pChannel->rgItems[i].wzDescription);
152 ReleaseStr(pChannel->rgItems[i].wzGuid);
153 ReleaseStr(pChannel->rgItems[i].wzEnclosureUrl);
154 ReleaseStr(pChannel->rgItems[i].wzEnclosureType);
155
156 FreeRssUnknownElementList(pChannel->rgItems[i].pUnknownElements);
157 }
158
159 ReleaseStr(pChannel->wzTitle);
160 ReleaseStr(pChannel->wzLink);
161 ReleaseStr(pChannel->wzDescription);
162 FreeRssUnknownElementList(pChannel->pUnknownElements);
163
164 MemFree(pChannel);
165 }
166}
167
168
169/********************************************************************
170 ParseRssDocument - parses out an RSS channel from a loaded XML DOM document.
171
172*********************************************************************/
173static HRESULT ParseRssDocument(
174 __in IXMLDOMDocument *pixd,
175 __out RSS_CHANNEL **ppChannel
176 )
177{
178 Assert(pixd);
179 Assert(ppChannel);
180
181 HRESULT hr = S_OK;
182 IXMLDOMElement *pRssElement = NULL;
183 IXMLDOMNodeList *pChannelNodes = NULL;
184 IXMLDOMNode *pNode = NULL;
185 BSTR bstrNodeName = NULL;
186
187 RSS_CHANNEL *pNewChannel = NULL;
188
189 //
190 // Get the document element and start processing channels.
191 //
192 hr = pixd ->get_documentElement(&pRssElement);
193 RssExitOnFailure(hr, "failed get_documentElement in ParseRssDocument");
194
195 hr = pRssElement->get_childNodes(&pChannelNodes);
196 RssExitOnFailure(hr, "Failed to get child nodes of Rss Document element.");
197
198 while (S_OK == (hr = XmlNextElement(pChannelNodes, &pNode, &bstrNodeName)))
199 {
200 if (0 == lstrcmpW(bstrNodeName, L"channel"))
201 {
202 hr = ParseRssChannel(pNode, &pNewChannel);
203 RssExitOnFailure(hr, "Failed to parse RSS channel.");
204 }
205 else if (0 == lstrcmpW(bstrNodeName, L"link"))
206 {
207 }
208
209 ReleaseNullBSTR(bstrNodeName);
210 ReleaseNullObject(pNode);
211 }
212
213 if (S_FALSE == hr)
214 {
215 hr = S_OK;
216 }
217
218 *ppChannel = pNewChannel;
219 pNewChannel = NULL;
220
221LExit:
222 ReleaseBSTR(bstrNodeName);
223 ReleaseObject(pNode);
224 ReleaseObject(pChannelNodes);
225 ReleaseObject(pRssElement);
226
227 ReleaseRssChannel(pNewChannel);
228
229 return hr;
230}
231
232
233/********************************************************************
234 ParseRssChannel - parses out an RSS channel from a loaded XML DOM element.
235
236*********************************************************************/
237static HRESULT ParseRssChannel(
238 __in IXMLDOMNode *pixnChannel,
239 __out RSS_CHANNEL **ppChannel
240 )
241{
242 Assert(pixnChannel);
243 Assert(ppChannel);
244
245 HRESULT hr = S_OK;
246 IXMLDOMNodeList *pNodeList = NULL;
247
248 RSS_CHANNEL *pNewChannel = NULL;
249 long cItems = 0;
250
251 IXMLDOMNode *pNode = NULL;
252 BSTR bstrNodeName = NULL;
253 BSTR bstrNodeValue = NULL;
254
255 //
256 // First, calculate how many RSS items we're going to have and allocate
257 // the RSS_CHANNEL structure
258 //
259 hr = XmlSelectNodes(pixnChannel, L"item", &pNodeList);
260 RssExitOnFailure(hr, "Failed to select all RSS items in an RSS channel.");
261
262 hr = pNodeList->get_length(&cItems);
263 RssExitOnFailure(hr, "Failed to count the number of RSS items in RSS channel.");
264
265 pNewChannel = static_cast<RSS_CHANNEL*>(MemAlloc(sizeof(RSS_CHANNEL) + sizeof(RSS_ITEM) * cItems, TRUE));
266 RssExitOnNull(pNewChannel, hr, E_OUTOFMEMORY, "Failed to allocate RSS channel structure.");
267
268 pNewChannel->cItems = cItems;
269
270 //
271 // Process the elements under a channel now.
272 //
273 hr = pixnChannel->get_childNodes(&pNodeList);
274 RssExitOnFailure(hr, "Failed to get child nodes of RSS channel element.");
275
276 cItems = 0; // reset the counter and use this to walk through the channel items
277 while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName)))
278 {
279 if (0 == lstrcmpW(bstrNodeName, L"title"))
280 {
281 hr = XmlGetText(pNode, &bstrNodeValue);
282 RssExitOnFailure(hr, "Failed to get RSS channel title.");
283
284 hr = StrAllocString(&pNewChannel->wzTitle, bstrNodeValue, 0);
285 RssExitOnFailure(hr, "Failed to allocate RSS channel title.");
286 }
287 else if (0 == lstrcmpW(bstrNodeName, L"link"))
288 {
289 hr = XmlGetText(pNode, &bstrNodeValue);
290 RssExitOnFailure(hr, "Failed to get RSS channel link.");
291
292 hr = StrAllocString(&pNewChannel->wzLink, bstrNodeValue, 0);
293 RssExitOnFailure(hr, "Failed to allocate RSS channel link.");
294 }
295 else if (0 == lstrcmpW(bstrNodeName, L"description"))
296 {
297 hr = XmlGetText(pNode, &bstrNodeValue);
298 RssExitOnFailure(hr, "Failed to get RSS channel description.");
299
300 hr = StrAllocString(&pNewChannel->wzDescription, bstrNodeValue, 0);
301 RssExitOnFailure(hr, "Failed to allocate RSS channel description.");
302 }
303 else if (0 == lstrcmpW(bstrNodeName, L"ttl"))
304 {
305 hr = XmlGetText(pNode, &bstrNodeValue);
306 RssExitOnFailure(hr, "Failed to get RSS channel description.");
307
308 pNewChannel->dwTimeToLive = (DWORD)wcstoul(bstrNodeValue, NULL, 10);
309 }
310 else if (0 == lstrcmpW(bstrNodeName, L"item"))
311 {
312 hr = ParseRssItem(pNode, cItems, pNewChannel);
313 RssExitOnFailure(hr, "Failed to parse RSS item.");
314
315 ++cItems;
316 }
317 else
318 {
319 hr = ParseRssUnknownElement(pNode, &pNewChannel->pUnknownElements);
320 RssExitOnFailure(hr, "Failed to parse unknown RSS channel element: %ls", bstrNodeName);
321 }
322
323 ReleaseNullBSTR(bstrNodeValue);
324 ReleaseNullBSTR(bstrNodeName);
325 ReleaseNullObject(pNode);
326 }
327
328 *ppChannel = pNewChannel;
329 pNewChannel = NULL;
330
331LExit:
332 ReleaseBSTR(bstrNodeName);
333 ReleaseObject(pNode);
334 ReleaseObject(pNodeList);
335
336 ReleaseRssChannel(pNewChannel);
337
338 return hr;
339}
340
341
342/********************************************************************
343 ParseRssItem - parses out an RSS item from a loaded XML DOM node.
344
345*********************************************************************/
346static HRESULT ParseRssItem(
347 __in IXMLDOMNode *pixnItem,
348 __in DWORD cItem,
349 __in_xcount(pChannel->cItems) RSS_CHANNEL *pChannel
350 )
351{
352 HRESULT hr = S_OK;
353
354 RSS_ITEM *pItem = NULL;
355 IXMLDOMNodeList *pNodeList = NULL;
356
357 IXMLDOMNode *pNode = NULL;
358 BSTR bstrNodeName = NULL;
359 BSTR bstrNodeValue = NULL;
360
361 //
362 // First make sure we're dealing with a valid item.
363 //
364 if (pChannel->cItems <= cItem)
365 {
366 hr = E_UNEXPECTED;
367 RssExitOnFailure(hr, "Unexpected number of items parsed.");
368 }
369
370 pItem = pChannel->rgItems + cItem;
371
372 //
373 // Process the elements under an item now.
374 //
375 hr = pixnItem->get_childNodes(&pNodeList);
376 RssExitOnFailure(hr, "Failed to get child nodes of RSS item element.");
377 while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, &bstrNodeName)))
378 {
379 if (0 == lstrcmpW(bstrNodeName, L"title"))
380 {
381 hr = XmlGetText(pNode, &bstrNodeValue);
382 RssExitOnFailure(hr, "Failed to get RSS channel title.");
383
384 hr = StrAllocString(&pItem->wzTitle, bstrNodeValue, 0);
385 RssExitOnFailure(hr, "Failed to allocate RSS item title.");
386 }
387 else if (0 == lstrcmpW(bstrNodeName, L"link"))
388 {
389 hr = XmlGetText(pNode, &bstrNodeValue);
390 RssExitOnFailure(hr, "Failed to get RSS channel link.");
391
392 hr = StrAllocString(&pItem->wzLink, bstrNodeValue, 0);
393 RssExitOnFailure(hr, "Failed to allocate RSS item link.");
394 }
395 else if (0 == lstrcmpW(bstrNodeName, L"description"))
396 {
397 hr = XmlGetText(pNode, &bstrNodeValue);
398 RssExitOnFailure(hr, "Failed to get RSS item description.");
399
400 hr = StrAllocString(&pItem->wzDescription, bstrNodeValue, 0);
401 RssExitOnFailure(hr, "Failed to allocate RSS item description.");
402 }
403 else if (0 == lstrcmpW(bstrNodeName, L"guid"))
404 {
405 hr = XmlGetText(pNode, &bstrNodeValue);
406 RssExitOnFailure(hr, "Failed to get RSS item guid.");
407
408 hr = StrAllocString(&pItem->wzGuid, bstrNodeValue, 0);
409 RssExitOnFailure(hr, "Failed to allocate RSS item guid.");
410 }
411 else if (0 == lstrcmpW(bstrNodeName, L"pubDate"))
412 {
413 hr = XmlGetText(pNode, &bstrNodeValue);
414 RssExitOnFailure(hr, "Failed to get RSS item guid.");
415
416 hr = TimeFromString(bstrNodeValue, &pItem->ftPublished);
417 RssExitOnFailure(hr, "Failed to convert RSS item time.");
418 }
419 else if (0 == lstrcmpW(bstrNodeName, L"enclosure"))
420 {
421 hr = XmlGetAttribute(pNode, L"url", &bstrNodeValue);
422 RssExitOnFailure(hr, "Failed to get RSS item enclosure url.");
423
424 hr = StrAllocString(&pItem->wzEnclosureUrl, bstrNodeValue, 0);
425 RssExitOnFailure(hr, "Failed to allocate RSS item enclosure url.");
426 ReleaseNullBSTR(bstrNodeValue);
427
428 hr = XmlGetAttributeNumber(pNode, L"length", &pItem->dwEnclosureSize);
429 RssExitOnFailure(hr, "Failed to get RSS item enclosure length.");
430
431 hr = XmlGetAttribute(pNode, L"type", &bstrNodeValue);
432 RssExitOnFailure(hr, "Failed to get RSS item enclosure type.");
433
434 hr = StrAllocString(&pItem->wzEnclosureType, bstrNodeValue, 0);
435 RssExitOnFailure(hr, "Failed to allocate RSS item enclosure type.");
436 }
437 else
438 {
439 hr = ParseRssUnknownElement(pNode, &pItem->pUnknownElements);
440 RssExitOnFailure(hr, "Failed to parse unknown RSS item element: %ls", bstrNodeName);
441 }
442
443 ReleaseNullBSTR(bstrNodeValue);
444 ReleaseNullBSTR(bstrNodeName);
445 ReleaseNullObject(pNode);
446 }
447
448LExit:
449 ReleaseBSTR(bstrNodeValue);
450 ReleaseBSTR(bstrNodeName);
451 ReleaseObject(pNode);
452 ReleaseObject(pNodeList);
453
454 return hr;
455}
456
457
458/********************************************************************
459 ParseRssUnknownElement - parses out an unknown item from the RSS feed from a loaded XML DOM node.
460
461*********************************************************************/
462static HRESULT ParseRssUnknownElement(
463 __in IXMLDOMNode *pNode,
464 __inout RSS_UNKNOWN_ELEMENT** ppUnknownElement
465 )
466{
467 Assert(ppUnknownElement);
468
469 HRESULT hr = S_OK;
470 BSTR bstrNodeNamespace = NULL;
471 BSTR bstrNodeName = NULL;
472 BSTR bstrNodeValue = NULL;
473 IXMLDOMNamedNodeMap* pixnnmAttributes = NULL;
474 IXMLDOMNode* pixnAttribute = NULL;
475 RSS_UNKNOWN_ELEMENT* pNewUnknownElement;
476
477 pNewUnknownElement = static_cast<RSS_UNKNOWN_ELEMENT*>(MemAlloc(sizeof(RSS_UNKNOWN_ELEMENT), TRUE));
478 RssExitOnNull(pNewUnknownElement, hr, E_OUTOFMEMORY, "Failed to allocate unknown element.");
479
480 hr = pNode->get_namespaceURI(&bstrNodeNamespace);
481 if (S_OK == hr)
482 {
483 hr = StrAllocString(&pNewUnknownElement->wzNamespace, bstrNodeNamespace, 0);
484 RssExitOnFailure(hr, "Failed to allocate RSS unknown element namespace.");
485 }
486 else if (S_FALSE == hr)
487 {
488 hr = S_OK;
489 }
490 RssExitOnFailure(hr, "Failed to get unknown element namespace.");
491
492 hr = pNode->get_baseName(&bstrNodeName);
493 RssExitOnFailure(hr, "Failed to get unknown element name.");
494
495 hr = StrAllocString(&pNewUnknownElement->wzElement, bstrNodeName, 0);
496 RssExitOnFailure(hr, "Failed to allocate RSS unknown element name.");
497
498 hr = XmlGetText(pNode, &bstrNodeValue);
499 RssExitOnFailure(hr, "Failed to get unknown element value.");
500
501 hr = StrAllocString(&pNewUnknownElement->wzValue, bstrNodeValue, 0);
502 RssExitOnFailure(hr, "Failed to allocate RSS unknown element value.");
503
504 hr = pNode->get_attributes(&pixnnmAttributes);
505 RssExitOnFailure(hr, "Failed get attributes on RSS unknown element.");
506
507 while (S_OK == (hr = pixnnmAttributes->nextNode(&pixnAttribute)))
508 {
509 hr = ParseRssUnknownAttribute(pixnAttribute, &pNewUnknownElement->pAttributes);
510 RssExitOnFailure(hr, "Failed to parse attribute on RSS unknown element.");
511
512 ReleaseNullObject(pixnAttribute);
513 }
514
515 if (S_FALSE == hr)
516 {
517 hr = S_OK;
518 }
519 RssExitOnFailure(hr, "Failed to enumerate all attributes on RSS unknown element.");
520
521 RSS_UNKNOWN_ELEMENT** ppTail = ppUnknownElement;
522 while (*ppTail)
523 {
524 ppTail = &(*ppTail)->pNext;
525 }
526
527 *ppTail = pNewUnknownElement;
528 pNewUnknownElement = NULL;
529
530LExit:
531 FreeRssUnknownElementList(pNewUnknownElement);
532
533 ReleaseBSTR(bstrNodeNamespace);
534 ReleaseBSTR(bstrNodeName);
535 ReleaseBSTR(bstrNodeValue);
536 ReleaseObject(pixnnmAttributes);
537 ReleaseObject(pixnAttribute);
538
539 return hr;
540}
541
542
543/********************************************************************
544 ParseRssUnknownAttribute - parses out attribute from an unknown element
545
546*********************************************************************/
547static HRESULT ParseRssUnknownAttribute(
548 __in IXMLDOMNode *pNode,
549 __inout RSS_UNKNOWN_ATTRIBUTE** ppUnknownAttribute
550 )
551{
552 Assert(ppUnknownAttribute);
553
554 HRESULT hr = S_OK;
555 BSTR bstrNodeNamespace = NULL;
556 BSTR bstrNodeName = NULL;
557 BSTR bstrNodeValue = NULL;
558 RSS_UNKNOWN_ATTRIBUTE* pNewUnknownAttribute;
559
560 pNewUnknownAttribute = static_cast<RSS_UNKNOWN_ATTRIBUTE*>(MemAlloc(sizeof(RSS_UNKNOWN_ATTRIBUTE), TRUE));
561 RssExitOnNull(pNewUnknownAttribute, hr, E_OUTOFMEMORY, "Failed to allocate unknown attribute.");
562
563 hr = pNode->get_namespaceURI(&bstrNodeNamespace);
564 if (S_OK == hr)
565 {
566 hr = StrAllocString(&pNewUnknownAttribute->wzNamespace, bstrNodeNamespace, 0);
567 RssExitOnFailure(hr, "Failed to allocate RSS unknown attribute namespace.");
568 }
569 else if (S_FALSE == hr)
570 {
571 hr = S_OK;
572 }
573 RssExitOnFailure(hr, "Failed to get unknown attribute namespace.");
574
575 hr = pNode->get_baseName(&bstrNodeName);
576 RssExitOnFailure(hr, "Failed to get unknown attribute name.");
577
578 hr = StrAllocString(&pNewUnknownAttribute->wzAttribute, bstrNodeName, 0);
579 RssExitOnFailure(hr, "Failed to allocate RSS unknown attribute name.");
580
581 hr = XmlGetText(pNode, &bstrNodeValue);
582 RssExitOnFailure(hr, "Failed to get unknown attribute value.");
583
584 hr = StrAllocString(&pNewUnknownAttribute->wzValue, bstrNodeValue, 0);
585 RssExitOnFailure(hr, "Failed to allocate RSS unknown attribute value.");
586
587 RSS_UNKNOWN_ATTRIBUTE** ppTail = ppUnknownAttribute;
588 while (*ppTail)
589 {
590 ppTail = &(*ppTail)->pNext;
591 }
592
593 *ppTail = pNewUnknownAttribute;
594 pNewUnknownAttribute = NULL;
595
596LExit:
597 FreeRssUnknownAttributeList(pNewUnknownAttribute);
598
599 ReleaseBSTR(bstrNodeNamespace);
600 ReleaseBSTR(bstrNodeName);
601 ReleaseBSTR(bstrNodeValue);
602
603 return hr;
604}
605
606
607/********************************************************************
608 FreeRssUnknownElement - releases all of the memory used by a list of unknown elements
609
610*********************************************************************/
611static void FreeRssUnknownElementList(
612 __in_opt RSS_UNKNOWN_ELEMENT* pUnknownElement
613 )
614{
615 while (pUnknownElement)
616 {
617 RSS_UNKNOWN_ELEMENT* pFree = pUnknownElement;
618 pUnknownElement = pUnknownElement->pNext;
619
620 FreeRssUnknownAttributeList(pFree->pAttributes);
621 ReleaseStr(pFree->wzNamespace);
622 ReleaseStr(pFree->wzElement);
623 ReleaseStr(pFree->wzValue);
624 MemFree(pFree);
625 }
626}
627
628
629/********************************************************************
630 FreeRssUnknownAttribute - releases all of the memory used by a list of unknown attributes
631
632*********************************************************************/
633static void FreeRssUnknownAttributeList(
634 __in_opt RSS_UNKNOWN_ATTRIBUTE* pUnknownAttribute
635 )
636{
637 while (pUnknownAttribute)
638 {
639 RSS_UNKNOWN_ATTRIBUTE* pFree = pUnknownAttribute;
640 pUnknownAttribute = pUnknownAttribute->pNext;
641
642 ReleaseStr(pFree->wzNamespace);
643 ReleaseStr(pFree->wzAttribute);
644 ReleaseStr(pFree->wzValue);
645 MemFree(pFree);
646 }
647}
diff --git a/src/libs/dutil/WixToolset.DUtil/sceutil.cpp b/src/libs/dutil/WixToolset.DUtil/sceutil.cpp
new file mode 100644
index 00000000..cdb1623b
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/sceutil.cpp
@@ -0,0 +1,2489 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5#include "sceutil.h"
6
7// Limit is documented as 4 GB, but for some reason the API's don't let us specify anything above 4091 MB.
8#define MAX_SQLCE_DATABASE_SIZE 4091
9
10// In case of some older versions of sqlce_oledb.h don't have these definitions, define some types.
11#ifndef DBTYPEFOR_DBLENGTH
12#ifdef _WIN64
13#define SKIP_SCE_COMPILE
14#else
15#define SCE_32BIT_ONLY
16typedef DWORD DBLENGTH;
17typedef LONG DBROWOFFSET;
18typedef LONG DBROWCOUNT;
19typedef DWORD DBCOUNTITEM;
20typedef DWORD DBORDINAL;
21typedef LONG DB_LORDINAL;
22typedef DWORD DBBKMARK;
23typedef DWORD DBBYTEOFFSET;
24typedef DWORD DBREFCOUNT;
25typedef DWORD DB_UPARAMS;
26typedef LONG DB_LPARAMS;
27typedef DWORD DBHASHVALUE;
28typedef DWORD DB_DWRESERVE;
29typedef LONG DB_LRESERVE;
30typedef DWORD DB_URESERVE;
31#endif
32
33#endif
34
35#ifndef SKIP_SCE_COMPILE // If the sce headers don't support 64-bit, don't build for 64-bit
36
37// structs
38struct SCE_DATABASE_INTERNAL
39{
40 // In case we call DllGetClassObject on a specific file
41 HMODULE hSqlCeDll;
42
43 volatile LONG dwTransactionRefcount;
44 IDBInitialize *pIDBInitialize;
45 IDBCreateSession *pIDBCreateSession;
46 ITransactionLocal *pITransactionLocal;
47 IDBProperties *pIDBProperties;
48 IOpenRowset *pIOpenRowset;
49 ISessionProperties *pISessionProperties;
50
51 BOOL fChanges; // This database has changed
52 BOOL fPendingChanges; // Some changes are pending, upon transaction commit
53 BOOL fRollbackTransaction; // If this flag is true, the current transaction was requested to be rolled back
54 BOOL fTransactionBadState; // If this flag is true, we were unable to get out of a transaction, so starting a new transaction should fail
55
56 // If the database was opened as read-only, we copied it here - so delete it on close
57 LPWSTR sczTempDbFile;
58};
59
60struct SCE_ROW
61{
62 SCE_DATABASE_INTERNAL *pDatabaseInternal;
63
64 SCE_TABLE_SCHEMA *pTableSchema;
65 IRowset *pIRowset;
66 HROW hRow;
67 BOOL fInserting;
68
69 DWORD dwBindingIndex;
70 DBBINDING *rgBinding;
71 SIZE_T cbOffset;
72 BYTE *pbData;
73};
74
75struct SCE_QUERY
76{
77 SCE_TABLE_SCHEMA *pTableSchema;
78 SCE_INDEX_SCHEMA *pIndexSchema;
79 SCE_DATABASE_INTERNAL *pDatabaseInternal;
80
81 // Accessor build-up members
82 DWORD dwBindingIndex;
83 DBBINDING *rgBinding;
84 SIZE_T cbOffset;
85 BYTE *pbData;
86};
87
88struct SCE_QUERY_RESULTS
89{
90 SCE_DATABASE_INTERNAL *pDatabaseInternal;
91 IRowset *pIRowset;
92 SCE_TABLE_SCHEMA *pTableSchema;
93};
94
95extern const int SCE_ROW_HANDLE_BYTES = sizeof(SCE_ROW);
96extern const int SCE_QUERY_HANDLE_BYTES = sizeof(SCE_QUERY);
97extern const int SCE_QUERY_RESULTS_HANDLE_BYTES = sizeof(SCE_QUERY_RESULTS);
98
99// The following is the internal Sce-maintained table to tell the identifier and version of the schema
100const SCE_COLUMN_SCHEMA SCE_INTERNAL_VERSION_TABLE_VERSION_COLUMN_SCHEMA[] =
101{
102 {
103 L"AppIdentifier",
104 DBTYPE_WSTR,
105 0,
106 FALSE,
107 TRUE,
108 FALSE,
109 NULL,
110 0,
111 0
112 },
113 {
114 L"Version",
115 DBTYPE_I4,
116 0,
117 FALSE,
118 FALSE,
119 FALSE,
120 NULL,
121 0,
122 0
123 }
124};
125
126const SCE_TABLE_SCHEMA SCE_INTERNAL_VERSION_TABLE_SCHEMA[] =
127{
128 L"SceSchemaTablev1",
129 _countof(SCE_INTERNAL_VERSION_TABLE_VERSION_COLUMN_SCHEMA),
130 (SCE_COLUMN_SCHEMA *)SCE_INTERNAL_VERSION_TABLE_VERSION_COLUMN_SCHEMA,
131 0,
132 NULL,
133 NULL,
134 NULL
135};
136
137// internal function declarations
138
139// Creates an instance of SQL Server CE object, returning IDBInitialize object
140// If a file path is provided in wzSqlCeDllPath parameter, it calls DllGetClassObject
141// on that file specifically. Otherwise it calls CoCreateInstance
142static HRESULT CreateSqlCe(
143 __in_z_opt LPCWSTR wzSqlCeDllPath,
144 __out IDBInitialize **ppIDBInitialize,
145 __out_opt HMODULE *phSqlCeDll
146 );
147static HRESULT RunQuery(
148 __in BOOL fRange,
149 __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE psqhHandle,
150 __out SCE_QUERY_RESULTS **ppsqrhHandle
151 );
152static HRESULT FillOutColumnDescFromSchema(
153 __in const SCE_COLUMN_SCHEMA *pSchema,
154 __out DBCOLUMNDESC pColumnDesc
155 );
156static HRESULT EnsureSchema(
157 __in SCE_DATABASE *pDatabase,
158 __in SCE_DATABASE_SCHEMA *pDatabaseSchema
159 );
160static HRESULT OpenSchema(
161 __in SCE_DATABASE *pDatabase,
162 __in SCE_DATABASE_SCHEMA *pdsSchema
163 );
164static HRESULT SetColumnValue(
165 __in const SCE_TABLE_SCHEMA *pTableSchema,
166 __in DWORD dwColumnIndex,
167 __in_bcount_opt(cbSize) const BYTE *pbData,
168 __in SIZE_T cbSize,
169 __inout DBBINDING *pBinding,
170 __inout SIZE_T *pcbOffset,
171 __inout BYTE **ppbBuffer
172 );
173static HRESULT GetColumnValue(
174 __in SCE_ROW *pRow,
175 __in DWORD dwColumnIndex,
176 __out_opt BYTE **ppbData,
177 __out SIZE_T *pcbSize
178 );
179static HRESULT GetColumnValueFixed(
180 __in SCE_ROW *pRow,
181 __in DWORD dwColumnIndex,
182 __in DWORD cbSize,
183 __out BYTE *pbData
184 );
185static HRESULT EnsureLocalColumnConstraints(
186 __in ITableDefinition *pTableDefinition,
187 __in DBID *pTableID,
188 __in SCE_TABLE_SCHEMA *pTableSchema
189 );
190static HRESULT EnsureForeignColumnConstraints(
191 __in ITableDefinition *pTableDefinition,
192 __in DBID *pTableID,
193 __in SCE_TABLE_SCHEMA *pTableSchema,
194 __in SCE_DATABASE_SCHEMA *pDatabaseSchema
195 );
196static HRESULT SetSessionProperties(
197 __in ISessionProperties *pISessionProperties
198 );
199static HRESULT GetDatabaseSchemaInfo(
200 __in SCE_DATABASE *pDatabase,
201 __out LPWSTR *psczSchemaType,
202 __out DWORD *pdwVersion
203 );
204static HRESULT SetDatabaseSchemaInfo(
205 __in SCE_DATABASE *pDatabase,
206 __in LPCWSTR wzSchemaType,
207 __in DWORD dwVersion
208 );
209static void ReleaseDatabase(
210 SCE_DATABASE *pDatabase
211 );
212static void ReleaseDatabaseInternal(
213 SCE_DATABASE_INTERNAL *pDatabaseInternal
214 );
215
216// function definitions
217extern "C" HRESULT DAPI SceCreateDatabase(
218 __in_z LPCWSTR sczFile,
219 __in_z_opt LPCWSTR wzSqlCeDllPath,
220 __out SCE_DATABASE **ppDatabase
221 )
222{
223 HRESULT hr = S_OK;
224 LPWSTR sczDirectory = NULL;
225 SCE_DATABASE *pNewSceDatabase = NULL;
226 SCE_DATABASE_INTERNAL *pNewSceDatabaseInternal = NULL;
227 IDBDataSourceAdmin *pIDBDataSourceAdmin = NULL;
228 DBPROPSET rgdbpDataSourcePropSet[2] = { };
229 DBPROP rgdbpDataSourceProp[2] = { };
230 DBPROP rgdbpDataSourceSsceProp[1] = { };
231
232 pNewSceDatabase = reinterpret_cast<SCE_DATABASE *>(MemAlloc(sizeof(SCE_DATABASE), TRUE));
233 ExitOnNull(pNewSceDatabase, hr, E_OUTOFMEMORY, "Failed to allocate SCE_DATABASE struct");
234
235 pNewSceDatabaseInternal = reinterpret_cast<SCE_DATABASE_INTERNAL *>(MemAlloc(sizeof(SCE_DATABASE_INTERNAL), TRUE));
236 ExitOnNull(pNewSceDatabaseInternal, hr, E_OUTOFMEMORY, "Failed to allocate SCE_DATABASE_INTERNAL struct");
237
238 pNewSceDatabase->sdbHandle = reinterpret_cast<void *>(pNewSceDatabaseInternal);
239
240 hr = CreateSqlCe(wzSqlCeDllPath, &pNewSceDatabaseInternal->pIDBInitialize, &pNewSceDatabaseInternal->hSqlCeDll);
241 ExitOnFailure(hr, "Failed to get IDBInitialize interface");
242
243 hr = pNewSceDatabaseInternal->pIDBInitialize->QueryInterface(IID_IDBDataSourceAdmin, reinterpret_cast<void **>(&pIDBDataSourceAdmin));
244 ExitOnFailure(hr, "Failed to get IDBDataSourceAdmin interface");
245
246 hr = PathGetDirectory(sczFile, &sczDirectory);
247 ExitOnFailure(hr, "Failed to get directory portion of path: %ls", sczFile);
248
249 hr = DirEnsureExists(sczDirectory, NULL);
250 ExitOnFailure(hr, "Failed to ensure directory exists: %ls", sczDirectory);
251
252 rgdbpDataSourceProp[0].dwPropertyID = DBPROP_INIT_DATASOURCE;
253 rgdbpDataSourceProp[0].dwOptions = DBPROPOPTIONS_REQUIRED;
254 rgdbpDataSourceProp[0].vValue.vt = VT_BSTR;
255 rgdbpDataSourceProp[0].vValue.bstrVal = ::SysAllocString(sczFile);
256
257 rgdbpDataSourceProp[1].dwPropertyID = DBPROP_INIT_MODE;
258 rgdbpDataSourceProp[1].dwOptions = DBPROPOPTIONS_REQUIRED;
259 rgdbpDataSourceProp[1].vValue.vt = VT_I4;
260 rgdbpDataSourceProp[1].vValue.lVal = DB_MODE_SHARE_DENY_NONE;
261
262 // SQL CE doesn't seem to allow us to specify DBPROP_INIT_PROMPT if we include any properties from DBPROPSET_SSCE_DBINIT
263 rgdbpDataSourcePropSet[0].guidPropertySet = DBPROPSET_DBINIT;
264 rgdbpDataSourcePropSet[0].rgProperties = rgdbpDataSourceProp;
265 rgdbpDataSourcePropSet[0].cProperties = _countof(rgdbpDataSourceProp);
266
267 rgdbpDataSourceSsceProp[0].dwPropertyID = DBPROP_SSCE_MAX_DATABASE_SIZE;
268 rgdbpDataSourceSsceProp[0].dwOptions = DBPROPOPTIONS_REQUIRED;
269 rgdbpDataSourceSsceProp[0].vValue.vt = VT_I4;
270 rgdbpDataSourceSsceProp[0].vValue.intVal = MAX_SQLCE_DATABASE_SIZE;
271
272 rgdbpDataSourcePropSet[1].guidPropertySet = DBPROPSET_SSCE_DBINIT;
273 rgdbpDataSourcePropSet[1].rgProperties = rgdbpDataSourceSsceProp;
274 rgdbpDataSourcePropSet[1].cProperties = _countof(rgdbpDataSourceSsceProp);
275
276 hr = pIDBDataSourceAdmin->CreateDataSource(_countof(rgdbpDataSourcePropSet), rgdbpDataSourcePropSet, NULL, IID_IUnknown, NULL);
277 ExitOnFailure(hr, "Failed to create data source");
278
279 hr = pNewSceDatabaseInternal->pIDBInitialize->QueryInterface(IID_IDBProperties, reinterpret_cast<void **>(&pNewSceDatabaseInternal->pIDBProperties));
280 ExitOnFailure(hr, "Failed to get IDBProperties interface");
281
282 hr = pNewSceDatabaseInternal->pIDBInitialize->QueryInterface(IID_IDBCreateSession, reinterpret_cast<void **>(&pNewSceDatabaseInternal->pIDBCreateSession));
283 ExitOnFailure(hr, "Failed to get IDBCreateSession interface");
284
285 hr = pNewSceDatabaseInternal->pIDBCreateSession->CreateSession(NULL, IID_ISessionProperties, reinterpret_cast<IUnknown **>(&pNewSceDatabaseInternal->pISessionProperties));
286 ExitOnFailure(hr, "Failed to get ISessionProperties interface");
287
288 hr = SetSessionProperties(pNewSceDatabaseInternal->pISessionProperties);
289 ExitOnFailure(hr, "Failed to set session properties");
290
291 hr = pNewSceDatabaseInternal->pISessionProperties->QueryInterface(IID_IOpenRowset, reinterpret_cast<void **>(&pNewSceDatabaseInternal->pIOpenRowset));
292 ExitOnFailure(hr, "Failed to get IOpenRowset interface");
293
294 hr = pNewSceDatabaseInternal->pISessionProperties->QueryInterface(IID_ITransactionLocal, reinterpret_cast<void **>(&pNewSceDatabaseInternal->pITransactionLocal));
295 ExitOnFailure(hr, "Failed to get ITransactionLocal interface");
296
297 *ppDatabase = pNewSceDatabase;
298 pNewSceDatabase = NULL;
299
300LExit:
301 ReleaseStr(sczDirectory);
302 ReleaseObject(pIDBDataSourceAdmin);
303 ReleaseDatabase(pNewSceDatabase);
304 ReleaseBSTR(rgdbpDataSourceProp[0].vValue.bstrVal);
305
306 return hr;
307}
308
309extern "C" HRESULT DAPI SceOpenDatabase(
310 __in_z LPCWSTR sczFile,
311 __in_z_opt LPCWSTR wzSqlCeDllPath,
312 __in LPCWSTR wzExpectedSchemaType,
313 __in DWORD dwExpectedVersion,
314 __out SCE_DATABASE **ppDatabase,
315 __in BOOL fReadOnly
316 )
317{
318 HRESULT hr = S_OK;
319 DWORD dwVersionFound = 0;
320 WCHAR wzTempDbFile[MAX_PATH];
321 LPCWSTR wzPathToOpen = NULL;
322 LPWSTR sczSchemaType = NULL;
323 SCE_DATABASE *pNewSceDatabase = NULL;
324 SCE_DATABASE_INTERNAL *pNewSceDatabaseInternal = NULL;
325 DBPROPSET rgdbpDataSourcePropSet[2] = { };
326 DBPROP rgdbpDataSourceProp[2] = { };
327 DBPROP rgdbpDataSourceSsceProp[1] = { };
328
329 pNewSceDatabase = reinterpret_cast<SCE_DATABASE *>(MemAlloc(sizeof(SCE_DATABASE), TRUE));
330 ExitOnNull(pNewSceDatabase, hr, E_OUTOFMEMORY, "Failed to allocate SCE_DATABASE struct");
331
332 pNewSceDatabaseInternal = reinterpret_cast<SCE_DATABASE_INTERNAL *>(MemAlloc(sizeof(SCE_DATABASE_INTERNAL), TRUE));
333 ExitOnNull(pNewSceDatabaseInternal, hr, E_OUTOFMEMORY, "Failed to allocate SCE_DATABASE_INTERNAL struct");
334
335 pNewSceDatabase->sdbHandle = reinterpret_cast<void *>(pNewSceDatabaseInternal);
336
337 hr = CreateSqlCe(wzSqlCeDllPath, &pNewSceDatabaseInternal->pIDBInitialize, &pNewSceDatabaseInternal->hSqlCeDll);
338 ExitOnFailure(hr, "Failed to get IDBInitialize interface");
339
340 hr = pNewSceDatabaseInternal->pIDBInitialize->QueryInterface(IID_IDBProperties, reinterpret_cast<void **>(&pNewSceDatabaseInternal->pIDBProperties));
341 ExitOnFailure(hr, "Failed to get IDBProperties interface");
342
343 // TODO: had trouble getting SQL CE to read a file read-only, so we're copying it to a temp path for now.
344 if (fReadOnly)
345 {
346 hr = DirCreateTempPath(PathFile(sczFile), (LPWSTR)wzTempDbFile, _countof(wzTempDbFile));
347 ExitOnFailure(hr, "Failed to get temp path");
348
349 hr = FileEnsureCopy(sczFile, (LPCWSTR)wzTempDbFile, TRUE);
350 ExitOnFailure(hr, "Failed to copy file to temp path");
351
352 hr = StrAllocString(&pNewSceDatabaseInternal->sczTempDbFile, (LPCWSTR)wzTempDbFile, 0);
353 ExitOnFailure(hr, "Failed to copy temp db file path");
354
355 wzPathToOpen = (LPCWSTR)wzTempDbFile;
356 }
357 else
358 {
359 wzPathToOpen = sczFile;
360 }
361
362 rgdbpDataSourceProp[0].dwPropertyID = DBPROP_INIT_DATASOURCE;
363 rgdbpDataSourceProp[0].dwOptions = DBPROPOPTIONS_REQUIRED;
364 rgdbpDataSourceProp[0].vValue.vt = VT_BSTR;
365 rgdbpDataSourceProp[0].vValue.bstrVal = ::SysAllocString(wzPathToOpen);
366
367 rgdbpDataSourceProp[1].dwPropertyID = DBPROP_INIT_MODE;
368 rgdbpDataSourceProp[1].dwOptions = DBPROPOPTIONS_REQUIRED;
369 rgdbpDataSourceProp[1].vValue.vt = VT_I4;
370 rgdbpDataSourceProp[1].vValue.lVal = DB_MODE_SHARE_DENY_NONE;
371
372 // SQL CE doesn't seem to allow us to specify DBPROP_INIT_PROMPT if we include any properties from DBPROPSET_SSCE_DBINIT
373 rgdbpDataSourcePropSet[0].guidPropertySet = DBPROPSET_DBINIT;
374 rgdbpDataSourcePropSet[0].rgProperties = rgdbpDataSourceProp;
375 rgdbpDataSourcePropSet[0].cProperties = _countof(rgdbpDataSourceProp);
376
377 rgdbpDataSourceSsceProp[0].dwPropertyID = DBPROP_SSCE_MAX_DATABASE_SIZE;
378 rgdbpDataSourceSsceProp[0].dwOptions = DBPROPOPTIONS_REQUIRED;
379 rgdbpDataSourceSsceProp[0].vValue.vt = VT_I4;
380 rgdbpDataSourceSsceProp[0].vValue.lVal = MAX_SQLCE_DATABASE_SIZE;
381
382 rgdbpDataSourcePropSet[1].guidPropertySet = DBPROPSET_SSCE_DBINIT;
383 rgdbpDataSourcePropSet[1].rgProperties = rgdbpDataSourceSsceProp;
384 rgdbpDataSourcePropSet[1].cProperties = _countof(rgdbpDataSourceSsceProp);
385
386 hr = pNewSceDatabaseInternal->pIDBProperties->SetProperties(_countof(rgdbpDataSourcePropSet), rgdbpDataSourcePropSet);
387 ExitOnFailure(hr, "Failed to set initial properties to open database");
388
389 hr = pNewSceDatabaseInternal->pIDBInitialize->Initialize();
390 ExitOnFailure(hr, "Failed to open database: %ls", sczFile);
391
392 hr = pNewSceDatabaseInternal->pIDBInitialize->QueryInterface(IID_IDBCreateSession, reinterpret_cast<void **>(&pNewSceDatabaseInternal->pIDBCreateSession));
393 ExitOnFailure(hr, "Failed to get IDBCreateSession interface");
394
395 hr = pNewSceDatabaseInternal->pIDBCreateSession->CreateSession(NULL, IID_ISessionProperties, reinterpret_cast<IUnknown **>(&pNewSceDatabaseInternal->pISessionProperties));
396 ExitOnFailure(hr, "Failed to get ISessionProperties interface");
397
398 hr = SetSessionProperties(pNewSceDatabaseInternal->pISessionProperties);
399 ExitOnFailure(hr, "Failed to set session properties");
400
401 hr = pNewSceDatabaseInternal->pISessionProperties->QueryInterface(IID_IOpenRowset, reinterpret_cast<void **>(&pNewSceDatabaseInternal->pIOpenRowset));
402 ExitOnFailure(hr, "Failed to get IOpenRowset interface");
403
404 hr = pNewSceDatabaseInternal->pISessionProperties->QueryInterface(IID_ITransactionLocal, reinterpret_cast<void **>(&pNewSceDatabaseInternal->pITransactionLocal));
405 ExitOnFailure(hr, "Failed to get ITransactionLocal interface");
406
407 hr = GetDatabaseSchemaInfo(pNewSceDatabase, &sczSchemaType, &dwVersionFound);
408 ExitOnFailure(hr, "Failed to find schema version of database");
409
410 if (CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, 0, sczSchemaType, -1, wzExpectedSchemaType, -1))
411 {
412 hr = HRESULT_FROM_WIN32(ERROR_BAD_FILE_TYPE);
413 ExitOnRootFailure(hr, "Tried to open wrong database type - expected type %ls, found type %ls", wzExpectedSchemaType, sczSchemaType);
414 }
415 else if (dwVersionFound != dwExpectedVersion)
416 {
417 hr = HRESULT_FROM_WIN32(ERROR_PRODUCT_VERSION);
418 ExitOnRootFailure(hr, "Tried to open wrong database schema version - expected version %u, found version %u", dwExpectedVersion, dwVersionFound);
419 }
420
421 *ppDatabase = pNewSceDatabase;
422 pNewSceDatabase = NULL;
423
424LExit:
425 ReleaseBSTR(rgdbpDataSourceProp[0].vValue.bstrVal);
426 ReleaseStr(sczSchemaType);
427 ReleaseDatabase(pNewSceDatabase);
428
429 return hr;
430}
431
432extern "C" HRESULT DAPI SceEnsureDatabase(
433 __in_z LPCWSTR sczFile,
434 __in_z_opt LPCWSTR wzSqlCeDllPath,
435 __in LPCWSTR wzSchemaType,
436 __in DWORD dwExpectedVersion,
437 __in SCE_DATABASE_SCHEMA *pdsSchema,
438 __out SCE_DATABASE **ppDatabase
439 )
440{
441 HRESULT hr = S_OK;
442 SCE_DATABASE *pDatabase = NULL;
443
444 if (FileExistsEx(sczFile, NULL))
445 {
446 hr = SceOpenDatabase(sczFile, wzSqlCeDllPath, wzSchemaType, dwExpectedVersion, &pDatabase, FALSE);
447 ExitOnFailure(hr, "Failed to open database while ensuring database exists: %ls", sczFile);
448 }
449 else
450 {
451 hr = SceCreateDatabase(sczFile, wzSqlCeDllPath, &pDatabase);
452 ExitOnFailure(hr, "Failed to create database while ensuring database exists: %ls", sczFile);
453
454 hr = SetDatabaseSchemaInfo(pDatabase, wzSchemaType, dwExpectedVersion);
455 ExitOnFailure(hr, "Failed to set schema version of database");
456 }
457
458 hr = EnsureSchema(pDatabase, pdsSchema);
459 ExitOnFailure(hr, "Failed to ensure schema is correct in database: %ls", sczFile);
460
461 // Keep a pointer to the schema in the SCE_DATABASE object for future reference
462 pDatabase->pdsSchema = pdsSchema;
463
464 *ppDatabase = pDatabase;
465 pDatabase = NULL;
466
467LExit:
468 ReleaseDatabase(pDatabase);
469
470 return hr;
471}
472
473extern "C" HRESULT DAPI SceIsTableEmpty(
474 __in SCE_DATABASE *pDatabase,
475 __in DWORD dwTableIndex,
476 __out BOOL *pfEmpty
477 )
478{
479 HRESULT hr = S_OK;
480 SCE_ROW_HANDLE row = NULL;
481
482 hr = SceGetFirstRow(pDatabase, dwTableIndex, &row);
483 if (E_NOTFOUND == hr)
484 {
485 *pfEmpty = TRUE;
486 ExitFunction1(hr = S_OK);
487 }
488 ExitOnFailure(hr, "Failed to get first row while checking if table is empty");
489
490 *pfEmpty = FALSE;
491
492LExit:
493 ReleaseSceRow(row);
494
495 return hr;
496}
497
498extern "C" HRESULT DAPI SceGetFirstRow(
499 __in SCE_DATABASE *pDatabase,
500 __in DWORD dwTableIndex,
501 __out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle
502 )
503{
504 HRESULT hr = S_OK;
505 DBCOUNTITEM cRowsObtained = 0;
506 HROW hRow = DB_NULL_HROW;
507 HROW *phRow = &hRow;
508 SCE_ROW *pRow = NULL;
509 SCE_TABLE_SCHEMA *pTable = &(pDatabase->pdsSchema->rgTables[dwTableIndex]);
510
511 hr = pTable->pIRowset->RestartPosition(DB_NULL_HCHAPTER);
512 ExitOnFailure(hr, "Failed to reset IRowset position to beginning");
513
514 hr = pTable->pIRowset->GetNextRows(DB_NULL_HCHAPTER, 0, 1, &cRowsObtained, &phRow);
515 if (DB_S_ENDOFROWSET == hr)
516 {
517 ExitFunction1(hr = E_NOTFOUND);
518 }
519 ExitOnFailure(hr, "Failed to get next first row");
520
521 pRow = reinterpret_cast<SCE_ROW *>(MemAlloc(sizeof(SCE_ROW), TRUE));
522 ExitOnNull(pRow, hr, E_OUTOFMEMORY, "Failed to allocate SCE_ROW struct");
523
524 pRow->pDatabaseInternal = reinterpret_cast<SCE_DATABASE_INTERNAL *>(pDatabase->sdbHandle);
525 pRow->hRow = hRow;
526 pRow->pTableSchema = pTable;
527 pRow->pIRowset = pTable->pIRowset;
528 pRow->pIRowset->AddRef();
529
530 *pRowHandle = reinterpret_cast<SCE_ROW_HANDLE>(pRow);
531
532LExit:
533 return hr;
534}
535
536HRESULT DAPI SceGetNextRow(
537 __in SCE_DATABASE *pDatabase,
538 __in DWORD dwTableIndex,
539 __out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle
540 )
541{
542 HRESULT hr = S_OK;
543 DBCOUNTITEM cRowsObtained = 0;
544 HROW hRow = DB_NULL_HROW;
545 HROW *phRow = &hRow;
546 SCE_ROW *pRow = NULL;
547 SCE_TABLE_SCHEMA *pTable = &(pDatabase->pdsSchema->rgTables[dwTableIndex]);
548
549 hr = pTable->pIRowset->GetNextRows(DB_NULL_HCHAPTER, 0, 1, &cRowsObtained, &phRow);
550 if (DB_S_ENDOFROWSET == hr)
551 {
552 ExitFunction1(hr = E_NOTFOUND);
553 }
554 ExitOnFailure(hr, "Failed to get next first row");
555
556 pRow = reinterpret_cast<SCE_ROW *>(MemAlloc(sizeof(SCE_ROW), TRUE));
557 ExitOnNull(pRow, hr, E_OUTOFMEMORY, "Failed to allocate SCE_ROW struct");
558
559 pRow->pDatabaseInternal = reinterpret_cast<SCE_DATABASE_INTERNAL *>(pDatabase->sdbHandle);
560 pRow->hRow = hRow;
561 pRow->pTableSchema = pTable;
562 pRow->pIRowset = pTable->pIRowset;
563 pRow->pIRowset->AddRef();
564
565 *pRowHandle = reinterpret_cast<SCE_ROW_HANDLE>(pRow);
566
567LExit:
568 return hr;
569}
570
571extern "C" HRESULT DAPI SceBeginTransaction(
572 __in SCE_DATABASE *pDatabase
573 )
574{
575 HRESULT hr = S_OK;
576 SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast<SCE_DATABASE_INTERNAL *>(pDatabase->sdbHandle);
577
578 if (pDatabaseInternal->fTransactionBadState)
579 {
580 // We're in a hosed transaction state - we can't start a new one
581 ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_RECOVERY_FAILURE));
582 }
583
584 ::InterlockedIncrement(&pDatabaseInternal->dwTransactionRefcount);
585
586 if (1 == pDatabaseInternal->dwTransactionRefcount)
587 {
588 hr = pDatabaseInternal->pITransactionLocal->StartTransaction(ISOLATIONLEVEL_SERIALIZABLE, 0, NULL, NULL);
589 ExitOnFailure(hr, "Failed to start transaction");
590 }
591
592LExit:
593 if (FAILED(hr))
594 {
595 ::InterlockedDecrement(&pDatabaseInternal->dwTransactionRefcount);
596 }
597
598 return hr;
599}
600
601extern "C" HRESULT DAPI SceCommitTransaction(
602 __in SCE_DATABASE *pDatabase
603 )
604{
605 HRESULT hr = S_OK;
606 SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast<SCE_DATABASE_INTERNAL *>(pDatabase->sdbHandle);
607 Assert(0 < pDatabaseInternal->dwTransactionRefcount);
608
609 ::InterlockedDecrement(&pDatabaseInternal->dwTransactionRefcount);
610
611 if (0 == pDatabaseInternal->dwTransactionRefcount)
612 {
613 if (pDatabaseInternal->fRollbackTransaction)
614 {
615 hr = pDatabaseInternal->pITransactionLocal->Abort(NULL, FALSE, FALSE);
616 ExitOnFailure(hr, "Failed to abort transaction");
617 }
618 else
619 {
620 hr = pDatabaseInternal->pITransactionLocal->Commit(FALSE, XACTTC_SYNC, 0);
621 ExitOnFailure(hr, "Failed to commit transaction");
622 }
623
624 if (pDatabaseInternal->fPendingChanges)
625 {
626 pDatabaseInternal->fPendingChanges = FALSE;
627 pDatabaseInternal->fChanges = TRUE;
628 }
629
630 pDatabaseInternal->fRollbackTransaction = FALSE;
631 }
632
633LExit:
634 // If we tried to commit and failed, the caller should subsequently call rollback
635 if (FAILED(hr))
636 {
637 ::InterlockedIncrement(&pDatabaseInternal->dwTransactionRefcount);
638 }
639
640 return hr;
641}
642
643extern "C" HRESULT DAPI SceRollbackTransaction(
644 __in SCE_DATABASE *pDatabase
645 )
646{
647 HRESULT hr = S_OK;
648 SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast<SCE_DATABASE_INTERNAL *>(pDatabase->sdbHandle);
649 Assert(0 < pDatabaseInternal->dwTransactionRefcount);
650
651 ::InterlockedDecrement(&pDatabaseInternal->dwTransactionRefcount);
652
653 if (0 == pDatabaseInternal->dwTransactionRefcount)
654 {
655 hr = pDatabaseInternal->pITransactionLocal->Abort(NULL, FALSE, FALSE);
656 ExitOnFailure(hr, "Failed to abort transaction");
657 pDatabaseInternal->fPendingChanges = FALSE;
658
659 pDatabaseInternal->fRollbackTransaction = FALSE;
660 }
661 else
662 {
663 pDatabaseInternal->fRollbackTransaction = TRUE;
664 }
665
666LExit:
667 // We're just in a bad state now. Don't increment the transaction refcount (what is the user going to do - call us again?)
668 // but mark the database as bad so the user gets an error if they try to start a new transaction.
669 if (FAILED(hr))
670 {
671 TraceError(hr, "Failed to rollback transaction");
672 pDatabaseInternal->fTransactionBadState = TRUE;
673 }
674
675 return hr;
676}
677
678extern "C" HRESULT DAPI SceDeleteRow(
679 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle
680 )
681{
682 HRESULT hr = S_OK;
683 SCE_ROW *pRow = reinterpret_cast<SCE_ROW *>(*pRowHandle);
684 IRowsetChange *pIRowsetChange = NULL;
685 DBROWSTATUS rowStatus = DBROWSTATUS_S_OK;
686
687 hr = pRow->pIRowset->QueryInterface(IID_IRowsetChange, reinterpret_cast<void **>(&pIRowsetChange));
688 ExitOnFailure(hr, "Failed to get IRowsetChange interface");
689
690 hr = pIRowsetChange->DeleteRows(DB_NULL_HCHAPTER, 1, &pRow->hRow, &rowStatus);
691 ExitOnFailure(hr, "Failed to delete row with status: %u", rowStatus);
692
693 ReleaseNullSceRow(*pRowHandle);
694
695LExit:
696 ReleaseObject(pIRowsetChange);
697
698 return hr;
699}
700
701extern "C" HRESULT DAPI ScePrepareInsert(
702 __in SCE_DATABASE *pDatabase,
703 __in DWORD dwTableIndex,
704 __out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle
705 )
706{
707 HRESULT hr = S_OK;
708 SCE_ROW *pRow = NULL;
709
710 pRow = reinterpret_cast<SCE_ROW *>(MemAlloc(sizeof(SCE_ROW), TRUE));
711 ExitOnNull(pRow, hr, E_OUTOFMEMORY, "Failed to allocate SCE_ROW struct");
712
713 pRow->pDatabaseInternal = reinterpret_cast<SCE_DATABASE_INTERNAL *>(pDatabase->sdbHandle);
714 pRow->hRow = DB_NULL_HROW;
715 pRow->pTableSchema = &(pDatabase->pdsSchema->rgTables[dwTableIndex]);
716 pRow->pIRowset = pRow->pTableSchema->pIRowset;
717 pRow->pIRowset->AddRef();
718 pRow->fInserting = TRUE;
719
720 *pRowHandle = reinterpret_cast<SCE_ROW_HANDLE>(pRow);
721 pRow = NULL;
722
723LExit:
724 ReleaseSceRow(pRow);
725
726 return hr;
727}
728
729extern "C" HRESULT DAPI SceFinishUpdate(
730 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle
731 )
732{
733 HRESULT hr = S_OK;
734 SCE_ROW *pRow = reinterpret_cast<SCE_ROW *>(rowHandle);
735 IAccessor *pIAccessor = NULL;
736 IRowsetChange *pIRowsetChange = NULL;
737 DBBINDSTATUS *rgBindStatus = NULL;
738 HACCESSOR hAccessor = DB_NULL_HACCESSOR;
739 HROW hRow = DB_NULL_HROW;
740
741 hr = pRow->pIRowset->QueryInterface(IID_IAccessor, reinterpret_cast<void **>(&pIAccessor));
742 ExitOnFailure(hr, "Failed to get IAccessor interface");
743
744// This can be used when stepping through the debugger to see bind failures
745#ifdef DEBUG
746 if (0 < pRow->dwBindingIndex)
747 {
748 hr = MemEnsureArraySize(reinterpret_cast<void **>(&rgBindStatus), pRow->dwBindingIndex, sizeof(DBBINDSTATUS), 0);
749 ExitOnFailure(hr, "Failed to ensure binding status array size");
750 }
751#endif
752
753 hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, pRow->dwBindingIndex, pRow->rgBinding, 0, &hAccessor, rgBindStatus);
754 ExitOnFailure(hr, "Failed to create accessor");
755
756 hr = pRow->pIRowset->QueryInterface(IID_IRowsetChange, reinterpret_cast<void **>(&pIRowsetChange));
757 ExitOnFailure(hr, "Failed to get IRowsetChange interface");
758
759 if (pRow->fInserting)
760 {
761 hr = pIRowsetChange->InsertRow(DB_NULL_HCHAPTER, hAccessor, pRow->pbData, &hRow);
762 ExitOnFailure(hr, "Failed to insert new row");
763
764 pRow->hRow = hRow;
765 ReleaseNullObject(pRow->pIRowset);
766 pRow->pIRowset = pRow->pTableSchema->pIRowset;
767 pRow->pIRowset->AddRef();
768 }
769 else
770 {
771 hr = pIRowsetChange->SetData(pRow->hRow, hAccessor, pRow->pbData);
772 ExitOnFailure(hr, "Failed to update existing row");
773 }
774
775 if (0 < pRow->pDatabaseInternal->dwTransactionRefcount)
776 {
777 pRow->pDatabaseInternal->fPendingChanges = TRUE;
778 }
779 else
780 {
781 pRow->pDatabaseInternal->fChanges = TRUE;
782 }
783
784LExit:
785 if (DB_NULL_HACCESSOR != hAccessor)
786 {
787 pIAccessor->ReleaseAccessor(hAccessor, NULL);
788 }
789 ReleaseMem(rgBindStatus);
790 ReleaseObject(pIAccessor);
791 ReleaseObject(pIRowsetChange);
792
793 return hr;
794}
795
796extern "C" HRESULT DAPI SceSetColumnBinary(
797 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle,
798 __in DWORD dwColumnIndex,
799 __in_bcount(cbBuffer) const BYTE* pbBuffer,
800 __in SIZE_T cbBuffer
801 )
802{
803 HRESULT hr = S_OK;
804 size_t cbAllocSize = 0;
805 SCE_ROW *pRow = reinterpret_cast<SCE_ROW *>(rowHandle);
806
807 hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize);
808 ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set binary, columns: %u", pRow->pTableSchema->cColumns);
809
810 if (NULL == pRow->rgBinding)
811 {
812 pRow->rgBinding = static_cast<DBBINDING *>(MemAlloc(cbAllocSize, TRUE));
813 ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer");
814 }
815
816 hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, pbBuffer, cbBuffer, &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData);
817 ExitOnFailure(hr, "Failed to set column value as binary");
818
819LExit:
820 return hr;
821}
822
823extern "C" HRESULT DAPI SceSetColumnDword(
824 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle,
825 __in DWORD dwColumnIndex,
826 __in const DWORD dwValue
827 )
828{
829 HRESULT hr = S_OK;
830 size_t cbAllocSize = 0;
831 SCE_ROW *pRow = reinterpret_cast<SCE_ROW *>(rowHandle);
832
833 hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize);
834 ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set dword, columns: %u", pRow->pTableSchema->cColumns);
835
836 if (NULL == pRow->rgBinding)
837 {
838 pRow->rgBinding = static_cast<DBBINDING *>(MemAlloc(cbAllocSize, TRUE));
839 ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer");
840 }
841
842 hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, reinterpret_cast<const BYTE *>(&dwValue), 4, &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData);
843 ExitOnFailure(hr, "Failed to set column value as binary");
844
845LExit:
846 return hr;
847}
848
849extern "C" HRESULT DAPI SceSetColumnQword(
850 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle,
851 __in DWORD dwColumnIndex,
852 __in const DWORD64 qwValue
853 )
854{
855 HRESULT hr = S_OK;
856 size_t cbAllocSize = 0;
857 SCE_ROW *pRow = reinterpret_cast<SCE_ROW *>(rowHandle);
858
859 hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize);
860 ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set qword, columns: %u", pRow->pTableSchema->cColumns);
861
862 if (NULL == pRow->rgBinding)
863 {
864 pRow->rgBinding = static_cast<DBBINDING *>(MemAlloc(cbAllocSize, TRUE));
865 ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer");
866 }
867
868 hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, reinterpret_cast<const BYTE *>(&qwValue), 8, &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData);
869 ExitOnFailure(hr, "Failed to set column value as qword");
870
871LExit:
872 return hr;
873}
874
875extern "C" HRESULT DAPI SceSetColumnBool(
876 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle,
877 __in DWORD dwColumnIndex,
878 __in const BOOL fValue
879 )
880{
881 HRESULT hr = S_OK;
882 size_t cbAllocSize = 0;
883 short int sValue = fValue ? 0xFFFF : 0x0000;
884 SCE_ROW *pRow = reinterpret_cast<SCE_ROW *>(rowHandle);
885
886 hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize);
887 ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set bool, columns: %u", pRow->pTableSchema->cColumns);
888
889 if (NULL == pRow->rgBinding)
890 {
891 pRow->rgBinding = static_cast<DBBINDING *>(MemAlloc(cbAllocSize, TRUE));
892 ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer");
893 }
894
895 hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, reinterpret_cast<const BYTE *>(&sValue), 2, &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData);
896 ExitOnFailure(hr, "Failed to set column value as binary");
897
898LExit:
899 return hr;
900}
901
902extern "C" HRESULT DAPI SceSetColumnString(
903 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle,
904 __in DWORD dwColumnIndex,
905 __in_z_opt LPCWSTR wzValue
906 )
907{
908 HRESULT hr = S_OK;
909 size_t cbAllocSize = 0;
910 SCE_ROW *pRow = reinterpret_cast<SCE_ROW *>(rowHandle);
911 SIZE_T cbSize = (NULL == wzValue) ? 0 : ((lstrlenW(wzValue) + 1) * sizeof(WCHAR));
912
913 hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize);
914 ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set string, columns: %u", pRow->pTableSchema->cColumns);
915
916 if (NULL == pRow->rgBinding)
917 {
918 pRow->rgBinding = static_cast<DBBINDING *>(MemAlloc(cbAllocSize, TRUE));
919 ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer");
920 }
921
922 hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, reinterpret_cast<const BYTE *>(wzValue), cbSize, &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData);
923 ExitOnFailure(hr, "Failed to set column value as string: %ls", wzValue);
924
925LExit:
926 return hr;
927}
928
929extern "C" HRESULT DAPI SceSetColumnNull(
930 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle,
931 __in DWORD dwColumnIndex
932 )
933{
934 HRESULT hr = S_OK;
935 size_t cbAllocSize = 0;
936 SCE_ROW *pRow = reinterpret_cast<SCE_ROW *>(rowHandle);
937
938 hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize);
939 ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set empty, columns: %u", pRow->pTableSchema->cColumns);
940
941 if (NULL == pRow->rgBinding)
942 {
943 pRow->rgBinding = static_cast<DBBINDING *>(MemAlloc(cbAllocSize, TRUE));
944 ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer");
945 }
946
947 hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, NULL, 0, &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData);
948 ExitOnFailure(hr, "Failed to set column value as empty value");
949
950LExit:
951 return hr;
952}
953
954extern "C" HRESULT DAPI SceSetColumnSystemTime(
955 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle,
956 __in DWORD dwColumnIndex,
957 __in const SYSTEMTIME *pst
958 )
959{
960 HRESULT hr = S_OK;
961 size_t cbAllocSize = 0;
962 DBTIMESTAMP dbTimeStamp = { };
963
964 SCE_ROW *pRow = reinterpret_cast<SCE_ROW *>(rowHandle);
965
966 hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize);
967 ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set systemtime, columns: %u", pRow->pTableSchema->cColumns);
968
969 if (NULL == pRow->rgBinding)
970 {
971 pRow->rgBinding = static_cast<DBBINDING *>(MemAlloc(cbAllocSize, TRUE));
972 ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer");
973 }
974
975 dbTimeStamp.year = pst->wYear;
976 dbTimeStamp.month = pst->wMonth;
977 dbTimeStamp.day = pst->wDay;
978 dbTimeStamp.hour = pst->wHour;
979 dbTimeStamp.minute = pst->wMinute;
980 dbTimeStamp.second = pst->wSecond;
981 // Don't use .fraction because milliseconds are not reliable in SQL CE. They are rounded to the nearest 1/300th of a second,
982 // and it is not supported (or at least I can't figure out how) to query for an exact timestamp if milliseconds
983 // are involved (even when rounded the way SQL CE returns them).
984
985 hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, reinterpret_cast<const BYTE *>(&dbTimeStamp), sizeof(dbTimeStamp), &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData);
986 ExitOnFailure(hr, "Failed to set column value as DBTIMESTAMP");
987
988LExit:
989 return hr;
990}
991
992extern "C" HRESULT DAPI SceGetColumnBinary(
993 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle,
994 __in DWORD dwColumnIndex,
995 __out_opt BYTE **ppbBuffer,
996 __inout SIZE_T *pcbBuffer
997 )
998{
999 HRESULT hr = S_OK;
1000 SCE_ROW *pRow = reinterpret_cast<SCE_ROW *>(rowReadHandle);
1001
1002 hr = GetColumnValue(pRow, dwColumnIndex, ppbBuffer, pcbBuffer);
1003 if (E_NOTFOUND == hr)
1004 {
1005 ExitFunction();
1006 }
1007 ExitOnFailure(hr, "Failed to get binary data out of column");
1008
1009LExit:
1010 return hr;
1011}
1012
1013extern "C" HRESULT DAPI SceGetColumnDword(
1014 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle,
1015 __in DWORD dwColumnIndex,
1016 __out DWORD *pdwValue
1017 )
1018{
1019 HRESULT hr = S_OK;
1020 SCE_ROW *pRow = reinterpret_cast<SCE_ROW *>(rowReadHandle);
1021
1022 hr = GetColumnValueFixed(pRow, dwColumnIndex, 4, reinterpret_cast<BYTE *>(pdwValue));
1023 if (E_NOTFOUND == hr)
1024 {
1025 ExitFunction();
1026 }
1027 ExitOnFailure(hr, "Failed to get dword data out of column");
1028
1029LExit:
1030 return hr;
1031}
1032
1033extern "C" HRESULT DAPI SceGetColumnQword(
1034 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle,
1035 __in DWORD dwColumnIndex,
1036 __in DWORD64 *pqwValue
1037 )
1038{
1039 HRESULT hr = S_OK;
1040 SCE_ROW *pRow = reinterpret_cast<SCE_ROW *>(rowReadHandle);
1041
1042 hr = GetColumnValueFixed(pRow, dwColumnIndex, 8, reinterpret_cast<BYTE *>(pqwValue));
1043 if (E_NOTFOUND == hr)
1044 {
1045 ExitFunction();
1046 }
1047 ExitOnFailure(hr, "Failed to get qword data out of column");
1048
1049LExit:
1050 return hr;
1051}
1052
1053extern "C" HRESULT DAPI SceGetColumnBool(
1054 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle,
1055 __in DWORD dwColumnIndex,
1056 __out BOOL *pfValue
1057 )
1058{
1059 HRESULT hr = S_OK;
1060 short int sValue = 0;
1061 SCE_ROW *pRow = reinterpret_cast<SCE_ROW *>(rowReadHandle);
1062
1063 hr = GetColumnValueFixed(pRow, dwColumnIndex, 2, reinterpret_cast<BYTE *>(&sValue));
1064 if (E_NOTFOUND == hr)
1065 {
1066 ExitFunction();
1067 }
1068 ExitOnFailure(hr, "Failed to get data out of column");
1069
1070 if (sValue == 0x0000)
1071 {
1072 *pfValue = FALSE;
1073 }
1074 else
1075 {
1076 *pfValue = TRUE;
1077 }
1078
1079LExit:
1080 return hr;
1081}
1082
1083extern "C" HRESULT DAPI SceGetColumnString(
1084 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle,
1085 __in DWORD dwColumnIndex,
1086 __out_z LPWSTR *psczValue
1087 )
1088{
1089 HRESULT hr = S_OK;
1090 SCE_ROW *pRow = reinterpret_cast<SCE_ROW *>(rowReadHandle);
1091 SIZE_T cbSize = 0;
1092
1093 hr = GetColumnValue(pRow, dwColumnIndex, reinterpret_cast<BYTE **>(psczValue), &cbSize);
1094 if (E_NOTFOUND == hr)
1095 {
1096 ExitFunction();
1097 }
1098 ExitOnFailure(hr, "Failed to get string data out of column");
1099
1100LExit:
1101 return hr;
1102}
1103
1104extern "C" HRESULT DAPI SceGetColumnSystemTime(
1105 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle,
1106 __in DWORD dwColumnIndex,
1107 __out SYSTEMTIME *pst
1108 )
1109{
1110 HRESULT hr = S_OK;
1111 SCE_ROW *pRow = reinterpret_cast<SCE_ROW *>(rowReadHandle);
1112 DBTIMESTAMP dbTimeStamp = { };
1113
1114 hr = GetColumnValueFixed(pRow, dwColumnIndex, sizeof(dbTimeStamp), reinterpret_cast<BYTE *>(&dbTimeStamp));
1115 if (E_NOTFOUND == hr)
1116 {
1117 ExitFunction();
1118 }
1119 ExitOnFailure(hr, "Failed to get string data out of column");
1120
1121 pst->wYear = dbTimeStamp.year;
1122 pst->wMonth = dbTimeStamp.month;
1123 pst->wDay = dbTimeStamp.day;
1124 pst->wHour = dbTimeStamp.hour;
1125 pst->wMinute = dbTimeStamp.minute;
1126 pst->wSecond = dbTimeStamp.second;
1127
1128LExit:
1129 return hr;
1130}
1131
1132extern "C" void DAPI SceCloseTable(
1133 __in SCE_TABLE_SCHEMA *pTable
1134 )
1135{
1136 ReleaseNullObject(pTable->pIRowsetChange);
1137 ReleaseNullObject(pTable->pIRowset);
1138}
1139
1140extern "C" BOOL DAPI SceDatabaseChanged(
1141 __in SCE_DATABASE *pDatabase
1142 )
1143{
1144 SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast<SCE_DATABASE_INTERNAL *>(pDatabase->sdbHandle);
1145
1146 return pDatabaseInternal->fChanges;
1147}
1148
1149void DAPI SceResetDatabaseChanged(
1150 __in SCE_DATABASE *pDatabase
1151 )
1152{
1153 SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast<SCE_DATABASE_INTERNAL *>(pDatabase->sdbHandle);
1154
1155 pDatabaseInternal->fChanges = FALSE;
1156}
1157
1158extern "C" HRESULT DAPI SceCloseDatabase(
1159 __in SCE_DATABASE *pDatabase
1160 )
1161{
1162 HRESULT hr = S_OK;
1163
1164 ReleaseDatabase(pDatabase);
1165
1166 return hr;
1167}
1168
1169extern "C" HRESULT DAPI SceBeginQuery(
1170 __in SCE_DATABASE *pDatabase,
1171 __in DWORD dwTableIndex,
1172 __in DWORD dwIndex,
1173 __deref_out_bcount(SCE_QUERY_HANDLE_BYTES) SCE_QUERY_HANDLE *psqhHandle
1174 )
1175{
1176 HRESULT hr = S_OK;
1177 size_t cbAllocSize = 0;
1178 SCE_QUERY *psq = static_cast<SCE_QUERY*>(MemAlloc(sizeof(SCE_QUERY), TRUE));
1179 ExitOnNull(psq, hr, E_OUTOFMEMORY, "Failed to allocate new sce query");
1180
1181 psq->pTableSchema = &(pDatabase->pdsSchema->rgTables[dwTableIndex]);
1182 psq->pIndexSchema = &(psq->pTableSchema->rgIndexes[dwIndex]);
1183 psq->pDatabaseInternal = reinterpret_cast<SCE_DATABASE_INTERNAL *>(pDatabase->sdbHandle);
1184
1185 hr = ::SizeTMult(sizeof(DBBINDING), psq->pTableSchema->cColumns, &cbAllocSize);
1186 ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to begin query, columns: %u", psq->pTableSchema->cColumns);
1187
1188 psq->rgBinding = static_cast<DBBINDING *>(MemAlloc(cbAllocSize, TRUE));
1189 ExitOnNull(psq, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for new sce query");
1190
1191 *psqhHandle = static_cast<SCE_QUERY_HANDLE>(psq);
1192 psq = NULL;
1193
1194LExit:
1195 ReleaseSceQuery(psq);
1196
1197 return hr;
1198}
1199
1200HRESULT DAPI SceSetQueryColumnBinary(
1201 __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle,
1202 __in_bcount(cbBuffer) const BYTE* pbBuffer,
1203 __in SIZE_T cbBuffer
1204 )
1205{
1206 HRESULT hr = S_OK;
1207 SCE_QUERY *pQuery = reinterpret_cast<SCE_QUERY *>(sqhHandle);
1208
1209 hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], pbBuffer, cbBuffer, &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData);
1210 ExitOnFailure(hr, "Failed to set query column value as binary of size: %u", cbBuffer);
1211
1212 ++(pQuery->dwBindingIndex);
1213
1214LExit:
1215 return hr;
1216}
1217
1218HRESULT DAPI SceSetQueryColumnDword(
1219 __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle,
1220 __in const DWORD dwValue
1221 )
1222{
1223 HRESULT hr = S_OK;
1224 SCE_QUERY *pQuery = reinterpret_cast<SCE_QUERY *>(sqhHandle);
1225
1226 hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], reinterpret_cast<const BYTE *>(&dwValue), 4, &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData);
1227 ExitOnFailure(hr, "Failed to set query column value as dword");
1228
1229 ++(pQuery->dwBindingIndex);
1230
1231LExit:
1232 return hr;
1233}
1234
1235HRESULT DAPI SceSetQueryColumnQword(
1236 __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle,
1237 __in const DWORD64 qwValue
1238 )
1239{
1240 HRESULT hr = S_OK;
1241 SCE_QUERY *pQuery = reinterpret_cast<SCE_QUERY *>(sqhHandle);
1242
1243 hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], reinterpret_cast<const BYTE *>(&qwValue), 8, &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData);
1244 ExitOnFailure(hr, "Failed to set query column value as qword");
1245
1246 ++(pQuery->dwBindingIndex);
1247
1248LExit:
1249 return hr;
1250}
1251
1252HRESULT DAPI SceSetQueryColumnBool(
1253 __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle,
1254 __in const BOOL fValue
1255 )
1256{
1257 HRESULT hr = S_OK;
1258 short int sValue = fValue ? 1 : 0;
1259 SCE_QUERY *pQuery = reinterpret_cast<SCE_QUERY *>(sqhHandle);
1260
1261 hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], reinterpret_cast<const BYTE *>(&sValue), 2, &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData);
1262 ExitOnFailure(hr, "Failed to set query column value as boolean");
1263
1264 ++(pQuery->dwBindingIndex);
1265
1266LExit:
1267 return hr;
1268}
1269
1270HRESULT DAPI SceSetQueryColumnString(
1271 __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle,
1272 __in_z_opt LPCWSTR wzString
1273 )
1274{
1275 HRESULT hr = S_OK;
1276 SCE_QUERY *pQuery = reinterpret_cast<SCE_QUERY *>(sqhHandle);
1277 SIZE_T cbSize = (NULL == wzString) ? 0 : ((lstrlenW(wzString) + 1) * sizeof(WCHAR));
1278
1279 hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], reinterpret_cast<const BYTE *>(wzString), cbSize, &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData);
1280 ExitOnFailure(hr, "Failed to set query column value as string");
1281
1282 ++(pQuery->dwBindingIndex);
1283
1284LExit:
1285 return hr;
1286}
1287
1288HRESULT DAPI SceSetQueryColumnSystemTime(
1289 __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle,
1290 __in const SYSTEMTIME *pst
1291 )
1292{
1293 HRESULT hr = S_OK;
1294 DBTIMESTAMP dbTimeStamp = { };
1295 SCE_QUERY *pQuery = reinterpret_cast<SCE_QUERY *>(sqhHandle);
1296
1297 dbTimeStamp.year = pst->wYear;
1298 dbTimeStamp.month = pst->wMonth;
1299 dbTimeStamp.day = pst->wDay;
1300 dbTimeStamp.hour = pst->wHour;
1301 dbTimeStamp.minute = pst->wMinute;
1302 dbTimeStamp.second = pst->wSecond;
1303
1304 hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], reinterpret_cast<const BYTE *>(&dbTimeStamp), sizeof(dbTimeStamp), &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData);
1305 ExitOnFailure(hr, "Failed to set query column value as DBTIMESTAMP");
1306
1307 ++(pQuery->dwBindingIndex);
1308
1309LExit:
1310 return hr;
1311}
1312
1313HRESULT DAPI SceSetQueryColumnEmpty(
1314 __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle
1315 )
1316{
1317 HRESULT hr = S_OK;
1318 SCE_QUERY *pQuery = reinterpret_cast<SCE_QUERY *>(sqhHandle);
1319
1320 hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], NULL, 0, &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData);
1321 ExitOnFailure(hr, "Failed to set query column value as empty value");
1322
1323 ++(pQuery->dwBindingIndex);
1324
1325LExit:
1326 return hr;
1327}
1328
1329HRESULT DAPI SceRunQueryExact(
1330 __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE *psqhHandle,
1331 __out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle
1332 )
1333{
1334 HRESULT hr = S_OK;
1335 SCE_QUERY_RESULTS *pQueryResults = NULL;
1336
1337 hr = RunQuery(FALSE, *psqhHandle, &pQueryResults);
1338 if (E_NOTFOUND == hr)
1339 {
1340 ExitFunction();
1341 }
1342 ExitOnFailure(hr, "Failed to run query exact");
1343
1344 hr = SceGetNextResultRow(reinterpret_cast<SCE_QUERY_RESULTS_HANDLE>(pQueryResults), pRowHandle);
1345 if (E_NOTFOUND == hr)
1346 {
1347 ExitFunction();
1348 }
1349 ExitOnFailure(hr, "Failed to get next row out of results");
1350
1351LExit:
1352 ReleaseNullSceQuery(*psqhHandle);
1353 ReleaseSceQueryResults(pQueryResults);
1354
1355 return hr;
1356}
1357
1358extern "C" HRESULT DAPI SceRunQueryRange(
1359 __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE *psqhHandle,
1360 __deref_out_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS_HANDLE *psqrhHandle
1361 )
1362{
1363 HRESULT hr = S_OK;
1364 SCE_QUERY_RESULTS **ppQueryResults = reinterpret_cast<SCE_QUERY_RESULTS **>(psqrhHandle);
1365
1366 hr = RunQuery(TRUE, *psqhHandle, ppQueryResults);
1367 if (E_NOTFOUND == hr)
1368 {
1369 ExitFunction();
1370 }
1371 ExitOnFailure(hr, "Failed to run query for range");
1372
1373LExit:
1374 ReleaseNullSceQuery(*psqhHandle);
1375
1376 return hr;
1377}
1378
1379extern "C" HRESULT DAPI SceGetNextResultRow(
1380 __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS_HANDLE sqrhHandle,
1381 __out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle
1382 )
1383{
1384 HRESULT hr = S_OK;
1385 HROW hRow = DB_NULL_HROW;
1386 HROW *phRow = &hRow;
1387 DBCOUNTITEM cRowsObtained = 0;
1388 SCE_ROW *pRow = NULL;
1389 SCE_QUERY_RESULTS *pQueryResults = reinterpret_cast<SCE_QUERY_RESULTS *>(sqrhHandle);
1390
1391 Assert(pRowHandle && (*pRowHandle == NULL));
1392
1393 hr = pQueryResults->pIRowset->GetNextRows(DB_NULL_HCHAPTER, 0, 1, &cRowsObtained, &phRow);
1394 if (DB_S_ENDOFROWSET == hr)
1395 {
1396 ExitFunction1(hr = E_NOTFOUND);
1397 }
1398 ExitOnFailure(hr, "Failed to get next first row");
1399
1400 pRow = reinterpret_cast<SCE_ROW *>(MemAlloc(sizeof(SCE_ROW), TRUE));
1401 ExitOnNull(pRow, hr, E_OUTOFMEMORY, "Failed to allocate SCE_ROW struct");
1402
1403 pRow->pDatabaseInternal = reinterpret_cast<SCE_DATABASE_INTERNAL *>(pQueryResults->pDatabaseInternal);
1404 pRow->hRow = hRow;
1405 pRow->pTableSchema = pQueryResults->pTableSchema;
1406 pRow->pIRowset = pQueryResults->pIRowset;
1407 pRow->pIRowset->AddRef();
1408
1409 *pRowHandle = reinterpret_cast<SCE_ROW_HANDLE>(pRow);
1410 pRow = NULL;
1411 hRow = DB_NULL_HROW;
1412
1413LExit:
1414 if (DB_NULL_HROW != hRow)
1415 {
1416 pQueryResults->pIRowset->ReleaseRows(1, &hRow, NULL, NULL, NULL);
1417 }
1418 ReleaseSceRow(pRow);
1419
1420 return hr;
1421}
1422
1423extern "C" void DAPI SceFreeRow(
1424 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle
1425 )
1426{
1427 SCE_ROW *pRow = reinterpret_cast<SCE_ROW *>(rowHandle);
1428
1429 if (DB_NULL_HROW != pRow->hRow)
1430 {
1431 pRow->pIRowset->ReleaseRows(1, &pRow->hRow, NULL, NULL, NULL);
1432 }
1433 ReleaseObject(pRow->pIRowset);
1434 ReleaseMem(pRow->rgBinding);
1435 ReleaseMem(pRow->pbData);
1436 ReleaseMem(pRow);
1437}
1438
1439void DAPI SceFreeQuery(
1440 __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle
1441 )
1442{
1443 SCE_QUERY *pQuery = reinterpret_cast<SCE_QUERY *>(sqhHandle);
1444
1445 ReleaseMem(pQuery->rgBinding);
1446 ReleaseMem(pQuery->pbData);
1447 ReleaseMem(pQuery);
1448}
1449
1450void DAPI SceFreeQueryResults(
1451 __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS_HANDLE sqrhHandle
1452 )
1453{
1454 SCE_QUERY_RESULTS *pQueryResults = reinterpret_cast<SCE_QUERY_RESULTS *>(sqrhHandle);
1455
1456 ReleaseObject(pQueryResults->pIRowset);
1457 ReleaseMem(pQueryResults);
1458}
1459
1460// internal function definitions
1461static HRESULT CreateSqlCe(
1462 __in_z_opt LPCWSTR wzSqlCeDllPath,
1463 __out IDBInitialize **ppIDBInitialize,
1464 __out_opt HMODULE *phSqlCeDll
1465 )
1466{
1467 HRESULT hr = S_OK;
1468 LPWSTR sczPath = NULL;
1469 LPWSTR sczDirectory = NULL;
1470 LPWSTR sczDllFullPath = NULL;
1471
1472 if (NULL == wzSqlCeDllPath)
1473 {
1474 hr = CoCreateInstance(CLSID_SQLSERVERCE, 0, CLSCTX_INPROC_SERVER, IID_IDBInitialize, reinterpret_cast<void **>(ppIDBInitialize));
1475 ExitOnFailure(hr, "Failed to get IDBInitialize interface");
1476 }
1477 else
1478 {
1479 // First try loading DLL from the path of our EXE
1480 hr = PathForCurrentProcess(&sczPath, NULL);
1481 ExitOnFailure(hr, "Failed to get path for current process");
1482
1483 hr = PathGetDirectory(sczPath, &sczDirectory);
1484 ExitOnFailure(hr, "Failed to get directory of current process");
1485
1486 hr = PathConcat(sczDirectory, wzSqlCeDllPath, &sczDllFullPath);
1487 ExitOnFailure(hr, "Failed to concatenate current directory and DLL filename");
1488
1489 *phSqlCeDll = ::LoadLibraryW(sczDllFullPath);
1490
1491 // If that failed, fallback to loading from current path
1492 if (NULL == *phSqlCeDll)
1493 {
1494 hr = DirGetCurrent(&sczDirectory);
1495 ExitOnFailure(hr, "Failed to get current directory");
1496
1497 hr = PathConcat(sczDirectory, wzSqlCeDllPath, &sczDllFullPath);
1498 ExitOnFailure(hr, "Failed to concatenate current directory and DLL filename");
1499
1500 *phSqlCeDll = ::LoadLibraryW(sczDllFullPath);
1501 ExitOnNullWithLastError(*phSqlCeDll, hr, "Failed to open Sql CE DLL: %ls", sczDllFullPath);
1502 }
1503
1504 HRESULT (WINAPI *pfnGetFactory)(REFCLSID, REFIID, void**);
1505 pfnGetFactory = (HRESULT (WINAPI *)(REFCLSID, REFIID, void**))GetProcAddress(*phSqlCeDll, "DllGetClassObject");
1506
1507 IClassFactory* pFactory = NULL;
1508 hr = pfnGetFactory(CLSID_SQLSERVERCE, IID_IClassFactory, (void**)&pFactory);
1509 ExitOnFailure(hr, "Failed to get factory for IID_IDBInitialize from DLL: %ls", sczDllFullPath);
1510 ExitOnNull(pFactory, hr, E_UNEXPECTED, "GetFactory returned success, but pFactory was NULL");
1511
1512 hr = pFactory->CreateInstance(NULL, IID_IDBInitialize, (void**)ppIDBInitialize);
1513 pFactory->Release();
1514 }
1515
1516LExit:
1517 ReleaseStr(sczPath);
1518 ReleaseStr(sczDirectory);
1519 ReleaseStr(sczDllFullPath);
1520
1521 return hr;
1522}
1523
1524static HRESULT RunQuery(
1525 __in BOOL fRange,
1526 __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE psqhHandle,
1527 __deref_out_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS **ppQueryResults
1528 )
1529{
1530 HRESULT hr = S_OK;
1531 DBID tableID = { };
1532 DBID indexID = { };
1533 IAccessor *pIAccessor = NULL;
1534 IRowsetIndex *pIRowsetIndex = NULL;
1535 IRowset *pIRowset = NULL;
1536 HACCESSOR hAccessor = DB_NULL_HACCESSOR;
1537 SCE_QUERY *pQuery = reinterpret_cast<SCE_QUERY *>(psqhHandle);
1538 SCE_QUERY_RESULTS *pQueryResults = NULL;
1539 DBPROPSET rgdbpIndexPropSet[1];
1540 DBPROP rgdbpIndexProp[1];
1541
1542 rgdbpIndexPropSet[0].cProperties = 1;
1543 rgdbpIndexPropSet[0].guidPropertySet = DBPROPSET_ROWSET;
1544 rgdbpIndexPropSet[0].rgProperties = rgdbpIndexProp;
1545
1546 rgdbpIndexProp[0].dwPropertyID = DBPROP_IRowsetIndex;
1547 rgdbpIndexProp[0].dwOptions = DBPROPOPTIONS_REQUIRED;
1548 rgdbpIndexProp[0].colid = DB_NULLID;
1549 rgdbpIndexProp[0].vValue.vt = VT_BOOL;
1550 rgdbpIndexProp[0].vValue.boolVal = VARIANT_TRUE;
1551
1552 tableID.eKind = DBKIND_NAME;
1553 tableID.uName.pwszName = const_cast<WCHAR *>(pQuery->pTableSchema->wzName);
1554
1555 indexID.eKind = DBKIND_NAME;
1556 indexID.uName.pwszName = const_cast<WCHAR *>(pQuery->pIndexSchema->wzName);
1557
1558 hr = pQuery->pDatabaseInternal->pIOpenRowset->OpenRowset(NULL, &tableID, &indexID, IID_IRowsetIndex, _countof(rgdbpIndexPropSet), rgdbpIndexPropSet, (IUnknown**) &pIRowsetIndex);
1559 ExitOnFailure(hr, "Failed to open IRowsetIndex");
1560
1561 hr = pIRowsetIndex->QueryInterface(IID_IRowset, reinterpret_cast<void **>(&pIRowset));
1562 ExitOnFailure(hr, "Failed to get IRowset interface from IRowsetIndex");
1563
1564 hr = pIRowset->QueryInterface(IID_IAccessor, reinterpret_cast<void **>(&pIAccessor));
1565 ExitOnFailure(hr, "Failed to get IAccessor interface");
1566
1567 hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, pQuery->dwBindingIndex, pQuery->rgBinding, 0, &hAccessor, NULL);
1568 ExitOnFailure(hr, "Failed to create accessor");
1569
1570 if (!fRange)
1571 {
1572 hr = pIRowsetIndex->Seek(hAccessor, pQuery->dwBindingIndex, pQuery->pbData, DBSEEK_FIRSTEQ);
1573 if (DB_E_NOTFOUND == hr)
1574 {
1575 ExitFunction1(hr = E_NOTFOUND);
1576 }
1577 ExitOnFailure(hr, "Failed to seek to record");
1578 }
1579 else
1580 {
1581 // If ALL columns in the index were specified, do a full key match
1582 if (pQuery->dwBindingIndex == pQuery->pIndexSchema->cColumns)
1583 {
1584 hr = pIRowsetIndex->SetRange(hAccessor, pQuery->dwBindingIndex, pQuery->pbData, 0, NULL, DBRANGE_MATCH);
1585 }
1586 else
1587 {
1588 // Otherwise, just match the specified keys.
1589 // We really want to use DBRANGE_MATCH_N_SHIFT here, but SQL CE doesn't appear to support it
1590 // So instead, we set the start and end to the same partial key, and then allow inclusive matching
1591 // This appears to accomplish the same thing
1592 hr = pIRowsetIndex->SetRange(hAccessor, pQuery->dwBindingIndex, pQuery->pbData, pQuery->dwBindingIndex, pQuery->pbData, 0);
1593 }
1594 if (DB_E_NOTFOUND == hr || E_NOTFOUND == hr)
1595 {
1596 ExitFunction1(hr = E_NOTFOUND);
1597 }
1598 ExitOnFailure(hr, "Failed to set range to find records");
1599 }
1600
1601 pQueryResults = reinterpret_cast<SCE_QUERY_RESULTS *>(MemAlloc(sizeof(SCE_QUERY_RESULTS), TRUE));
1602 ExitOnNull(pQueryResults, hr, E_OUTOFMEMORY, "Failed to allocate query results struct");
1603
1604 pQueryResults->pDatabaseInternal = pQuery->pDatabaseInternal;
1605 pQueryResults->pTableSchema = pQuery->pTableSchema;
1606 pQueryResults->pIRowset = pIRowset;
1607 pIRowset = NULL;
1608
1609 *ppQueryResults = pQueryResults;
1610 pQueryResults = NULL;
1611
1612LExit:
1613 if (DB_NULL_HACCESSOR != hAccessor)
1614 {
1615 pIAccessor->ReleaseAccessor(hAccessor, NULL);
1616 }
1617 ReleaseObject(pIAccessor);
1618 ReleaseObject(pIRowset);
1619 ReleaseObject(pIRowsetIndex);
1620 ReleaseMem(pQueryResults);
1621 ReleaseSceQueryResults(pQueryResults);
1622
1623 return hr;
1624}
1625
1626static HRESULT FillOutColumnDescFromSchema(
1627 __in const SCE_COLUMN_SCHEMA *pColumnSchema,
1628 __out DBCOLUMNDESC *pColumnDesc
1629 )
1630{
1631 HRESULT hr = S_OK;
1632 DWORD dwColumnProperties = 0;
1633 DWORD dwColumnPropertyIndex = 0;
1634 BOOL fFixedSize = FALSE;
1635
1636 pColumnDesc->dbcid.eKind = DBKIND_NAME;
1637 pColumnDesc->dbcid.uName.pwszName = (WCHAR *)pColumnSchema->wzName;
1638 pColumnDesc->wType = pColumnSchema->dbtColumnType;
1639 pColumnDesc->ulColumnSize = pColumnSchema->dwLength;
1640 if (0 == pColumnDesc->ulColumnSize && (DBTYPE_WSTR == pColumnDesc->wType || DBTYPE_BYTES == pColumnDesc->wType))
1641 {
1642 fFixedSize = FALSE;
1643 }
1644 else
1645 {
1646 fFixedSize = TRUE;
1647 }
1648
1649 dwColumnProperties = 1;
1650 if (pColumnSchema->fAutoIncrement)
1651 {
1652 ++dwColumnProperties;
1653 }
1654 if (!pColumnSchema->fNullable)
1655 {
1656 ++dwColumnProperties;
1657 }
1658
1659 if (0 < dwColumnProperties)
1660 {
1661 pColumnDesc->cPropertySets = 1;
1662 pColumnDesc->rgPropertySets = reinterpret_cast<DBPROPSET *>(MemAlloc(sizeof(DBPROPSET), TRUE));
1663 ExitOnNull(pColumnDesc->rgPropertySets, hr, E_OUTOFMEMORY, "Failed to allocate propset object while setting up column parameters");
1664
1665 pColumnDesc->rgPropertySets[0].cProperties = dwColumnProperties;
1666 pColumnDesc->rgPropertySets[0].guidPropertySet = DBPROPSET_COLUMN;
1667 pColumnDesc->rgPropertySets[0].rgProperties = reinterpret_cast<DBPROP *>(MemAlloc(sizeof(DBPROP) * dwColumnProperties, TRUE));
1668
1669 dwColumnPropertyIndex = 0;
1670 if (pColumnSchema->fAutoIncrement)
1671 {
1672 pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].dwPropertyID = DBPROP_COL_AUTOINCREMENT;
1673 pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].dwOptions = DBPROPOPTIONS_REQUIRED;
1674 pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].colid = DB_NULLID;
1675 pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].vValue.vt = VT_BOOL;
1676 pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].vValue.boolVal = VARIANT_TRUE;
1677 ++dwColumnPropertyIndex;
1678 }
1679 if (!pColumnSchema->fNullable)
1680 {
1681 pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].dwPropertyID = DBPROP_COL_NULLABLE;
1682 pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].dwOptions = DBPROPOPTIONS_REQUIRED;
1683 pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].colid = DB_NULLID;
1684 pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].vValue.vt = VT_BOOL;
1685 pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].vValue.boolVal = VARIANT_FALSE;
1686 ++dwColumnPropertyIndex;
1687 }
1688
1689 pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].dwPropertyID = DBPROP_COL_FIXEDLENGTH;
1690 pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].dwOptions = DBPROPOPTIONS_REQUIRED;
1691 pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].colid = DB_NULLID;
1692 pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].vValue.vt = VT_BOOL;
1693 pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].vValue.boolVal = fFixedSize ? VARIANT_TRUE : VARIANT_FALSE;
1694 ++dwColumnPropertyIndex;
1695 }
1696
1697LExit:
1698 return hr;
1699}
1700
1701static HRESULT EnsureSchema(
1702 __in SCE_DATABASE *pDatabase,
1703 __in SCE_DATABASE_SCHEMA *pdsSchema
1704 )
1705{
1706 HRESULT hr = S_OK;
1707 size_t cbAllocSize = 0;
1708 BOOL fInTransaction = FALSE;
1709 BOOL fSchemaNeedsSetup = TRUE;
1710 DBID tableID = { };
1711 DBID indexID = { };
1712 DBPROPSET rgdbpIndexPropSet[1];
1713 DBPROPSET rgdbpRowSetPropSet[1];
1714 DBPROP rgdbpIndexProp[1];
1715 DBPROP rgdbpRowSetProp[1];
1716 DBCOLUMNDESC *rgColumnDescriptions = NULL;
1717 DBINDEXCOLUMNDESC *rgIndexColumnDescriptions = NULL;
1718 DWORD cIndexColumnDescriptions = 0;
1719 DWORD dwTableColumnIndex = 0;
1720 SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast<SCE_DATABASE_INTERNAL *>(pDatabase->sdbHandle);
1721 ITableDefinition *pTableDefinition = NULL;
1722 IIndexDefinition *pIndexDefinition = NULL;
1723 IRowsetIndex *pIRowsetIndex = NULL;
1724
1725 rgdbpRowSetPropSet[0].cProperties = 1;
1726 rgdbpRowSetPropSet[0].guidPropertySet = DBPROPSET_ROWSET;
1727 rgdbpRowSetPropSet[0].rgProperties = rgdbpRowSetProp;
1728
1729 rgdbpRowSetProp[0].dwPropertyID = DBPROP_IRowsetChange;
1730 rgdbpRowSetProp[0].dwOptions = DBPROPOPTIONS_REQUIRED;
1731 rgdbpRowSetProp[0].colid = DB_NULLID;
1732 rgdbpRowSetProp[0].vValue.vt = VT_BOOL;
1733 rgdbpRowSetProp[0].vValue.boolVal = VARIANT_TRUE;
1734
1735 rgdbpIndexPropSet[0].cProperties = 1;
1736 rgdbpIndexPropSet[0].guidPropertySet = DBPROPSET_INDEX;
1737 rgdbpIndexPropSet[0].rgProperties = rgdbpIndexProp;
1738
1739 rgdbpIndexProp[0].dwPropertyID = DBPROP_INDEX_NULLS;
1740 rgdbpIndexProp[0].dwOptions = DBPROPOPTIONS_REQUIRED;
1741 rgdbpIndexProp[0].colid = DB_NULLID;
1742 rgdbpIndexProp[0].vValue.vt = VT_I4;
1743 rgdbpIndexProp[0].vValue.lVal = DBPROPVAL_IN_DISALLOWNULL;
1744
1745 hr = pDatabaseInternal->pISessionProperties->QueryInterface(IID_ITableDefinition, reinterpret_cast<void **>(&pTableDefinition));
1746 ExitOnFailure(hr, "Failed to get ITableDefinition for table creation");
1747
1748 hr = pDatabaseInternal->pISessionProperties->QueryInterface(IID_IIndexDefinition, reinterpret_cast<void **>(&pIndexDefinition));
1749 ExitOnFailure(hr, "Failed to get IIndexDefinition for index creation");
1750
1751 hr = SceBeginTransaction(pDatabase);
1752 ExitOnFailure(hr, "Failed to start transaction for ensuring schema");
1753 fInTransaction = TRUE;
1754
1755 for (DWORD dwTable = 0; dwTable < pdsSchema->cTables; ++dwTable)
1756 {
1757 tableID.eKind = DBKIND_NAME;
1758 tableID.uName.pwszName = const_cast<WCHAR *>(pdsSchema->rgTables[dwTable].wzName);
1759
1760 // Fill out each column description struct as appropriate, to be used for creating the table, or confirming the table's columns all exist
1761 rgColumnDescriptions = static_cast<DBCOLUMNDESC *>(MemAlloc(sizeof(DBCOLUMNDESC) * pdsSchema->rgTables[dwTable].cColumns, TRUE));
1762 ExitOnNull(rgColumnDescriptions, hr, E_OUTOFMEMORY, "Failed to allocate column description array while creating table");
1763
1764 for (DWORD i = 0; i < pdsSchema->rgTables[dwTable].cColumns; ++i)
1765 {
1766 hr = FillOutColumnDescFromSchema(pdsSchema->rgTables[dwTable].rgColumns + i, rgColumnDescriptions + i);
1767 ExitOnFailure(hr, "Failed to fill out column description from schema");
1768 }
1769
1770 // First try to open the table - or if it doesn't exist, create it
1771 hr = pDatabaseInternal->pIOpenRowset->OpenRowset(NULL, &tableID, NULL, IID_IRowset, _countof(rgdbpRowSetPropSet), rgdbpRowSetPropSet, reinterpret_cast<IUnknown **>(&pdsSchema->rgTables[dwTable].pIRowset));
1772 if (DB_E_NOTABLE == hr)
1773 {
1774 // The table doesn't exist, so let's create it
1775 hr = pTableDefinition->CreateTable(NULL, &tableID, pdsSchema->rgTables[dwTable].cColumns, rgColumnDescriptions, IID_IUnknown, _countof(rgdbpRowSetPropSet), rgdbpRowSetPropSet, NULL, NULL);
1776 ExitOnFailure(hr, "Failed to create table: %ls", pdsSchema->rgTables[dwTable].wzName);
1777 }
1778 else
1779 {
1780 ExitOnFailure(hr, "Failed to open table %ls while ensuring schema", tableID.uName.pwszName);
1781
1782 // Close any rowset we opened
1783 ReleaseNullObject(pdsSchema->rgTables[dwTable].pIRowset);
1784
1785 // If it does exist, make sure all columns are in the table
1786 // Only nullable columns can be added to an existing table
1787 for (DWORD i = 1; i < pdsSchema->rgTables[dwTable].cColumns; ++i)
1788 {
1789 if (pdsSchema->rgTables[dwTable].rgColumns[i].fNullable)
1790 hr = pTableDefinition->AddColumn(&tableID, rgColumnDescriptions + i, NULL);
1791 if (DB_E_DUPLICATECOLUMNID == hr)
1792 {
1793 hr = S_OK;
1794 }
1795 ExitOnFailure(hr, "Failed to add column %ls", pdsSchema->rgTables[dwTable].rgColumns[i].wzName);
1796 }
1797 }
1798
1799#pragma prefast(push)
1800#pragma prefast(disable:26010)
1801 hr = EnsureLocalColumnConstraints(pTableDefinition, &tableID, pdsSchema->rgTables + dwTable);
1802#pragma prefast(pop)
1803 ExitOnFailure(hr, "Failed to ensure local column constraints for table: %ls", pdsSchema->rgTables[dwTable].wzName);
1804
1805 for (DWORD i = 0; i < pdsSchema->rgTables[dwTable].cColumns; ++i)
1806 {
1807 if (NULL != rgColumnDescriptions[i].rgPropertySets)
1808 {
1809 ReleaseMem(rgColumnDescriptions[i].rgPropertySets[0].rgProperties);
1810 ReleaseMem(rgColumnDescriptions[i].rgPropertySets);
1811 }
1812 }
1813
1814 ReleaseNullMem(rgColumnDescriptions);
1815 if (0 < pdsSchema->rgTables[dwTable].cIndexes)
1816 {
1817 // Now create indexes for the table
1818 for (DWORD dwIndex = 0; dwIndex < pdsSchema->rgTables[dwTable].cIndexes; ++dwIndex)
1819 {
1820 indexID.eKind = DBKIND_NAME;
1821 indexID.uName.pwszName = pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].wzName;
1822
1823 // Check if the index exists
1824 hr = pDatabaseInternal->pIOpenRowset->OpenRowset(NULL, &tableID, &indexID, IID_IRowsetIndex, 0, NULL, (IUnknown**) &pIRowsetIndex);
1825 if (SUCCEEDED(hr))
1826 {
1827 // TODO: If one with the same name exists, check if the schema actually matches
1828 ReleaseNullObject(pIRowsetIndex);
1829 continue;
1830 }
1831 hr = S_OK;
1832
1833 hr = ::SizeTMult(sizeof(DBINDEXCOLUMNDESC), pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].cColumns, &cbAllocSize);
1834 ExitOnFailure(hr, "Overflow while calculating allocation size for DBINDEXCOLUMNDESC, columns: %u", pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].cColumns);
1835
1836 rgIndexColumnDescriptions = reinterpret_cast<DBINDEXCOLUMNDESC *>(MemAlloc(cbAllocSize, TRUE));
1837 ExitOnNull(rgIndexColumnDescriptions, hr, E_OUTOFMEMORY, "Failed to allocate structure to hold index column descriptions");
1838 cIndexColumnDescriptions = pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].cColumns;
1839
1840 for (DWORD dwColumnIndex = 0; dwColumnIndex < cIndexColumnDescriptions; ++dwColumnIndex)
1841 {
1842 dwTableColumnIndex = pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].rgColumns[dwColumnIndex];
1843
1844 rgIndexColumnDescriptions[dwColumnIndex].pColumnID = reinterpret_cast<DBID *>(MemAlloc(sizeof(DBID), TRUE));
1845 rgIndexColumnDescriptions[dwColumnIndex].pColumnID->eKind = DBKIND_NAME;
1846 rgIndexColumnDescriptions[dwColumnIndex].pColumnID->uName.pwszName = const_cast<LPOLESTR>(pdsSchema->rgTables[dwTable].rgColumns[dwTableColumnIndex].wzName);
1847 rgIndexColumnDescriptions[dwColumnIndex].eIndexColOrder = pdsSchema->rgTables[dwTable].rgColumns[dwTableColumnIndex].fDescending ? DBINDEX_COL_ORDER_DESC : DBINDEX_COL_ORDER_ASC;
1848 }
1849
1850 hr = pIndexDefinition->CreateIndex(&tableID, &indexID, static_cast<DBORDINAL>(pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].cColumns), rgIndexColumnDescriptions, 1, rgdbpIndexPropSet, NULL);
1851 if (DB_E_DUPLICATEINDEXID == hr)
1852 {
1853 // If the index already exists, no worries
1854 hr = S_OK;
1855 }
1856 ExitOnFailure(hr, "Failed to create index named %ls into table named %ls", pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].wzName, pdsSchema->rgTables[dwTable].wzName);
1857
1858 for (DWORD i = 0; i < cIndexColumnDescriptions; ++i)
1859 {
1860 ReleaseMem(rgIndexColumnDescriptions[i].pColumnID);
1861 }
1862
1863 cIndexColumnDescriptions = 0;
1864 ReleaseNullMem(rgIndexColumnDescriptions);
1865 }
1866 }
1867 }
1868
1869 // Now once all tables have been created, create foreign key relationships
1870 if (fSchemaNeedsSetup)
1871 {
1872 for (DWORD dwTable = 0; dwTable < pdsSchema->cTables; ++dwTable)
1873 {
1874 tableID.eKind = DBKIND_NAME;
1875 tableID.uName.pwszName = const_cast<WCHAR *>(pdsSchema->rgTables[dwTable].wzName);
1876
1877 // Setup any constraints for the table's columns
1878 hr = EnsureForeignColumnConstraints(pTableDefinition, &tableID, pdsSchema->rgTables + dwTable, pdsSchema);
1879 ExitOnFailure(hr, "Failed to ensure foreign column constraints for table: %ls", pdsSchema->rgTables[dwTable].wzName);
1880 }
1881 }
1882
1883 hr = SceCommitTransaction(pDatabase);
1884 ExitOnFailure(hr, "Failed to commit transaction for ensuring schema");
1885 fInTransaction = FALSE;
1886
1887 hr = OpenSchema(pDatabase, pdsSchema);
1888 ExitOnFailure(hr, "Failed to open schema");
1889
1890LExit:
1891 ReleaseObject(pTableDefinition);
1892 ReleaseObject(pIndexDefinition);
1893 ReleaseObject(pIRowsetIndex);
1894
1895 if (fInTransaction)
1896 {
1897 SceRollbackTransaction(pDatabase);
1898 }
1899
1900 for (DWORD i = 0; i < cIndexColumnDescriptions; ++i)
1901 {
1902 ReleaseMem(rgIndexColumnDescriptions[i].pColumnID);
1903 }
1904
1905 ReleaseMem(rgIndexColumnDescriptions);
1906 ReleaseMem(rgColumnDescriptions);
1907
1908 return hr;
1909}
1910
1911static HRESULT OpenSchema(
1912 __in SCE_DATABASE *pDatabase,
1913 __in SCE_DATABASE_SCHEMA *pdsSchema
1914 )
1915{
1916 HRESULT hr = S_OK;
1917 SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast<SCE_DATABASE_INTERNAL *>(pDatabase->sdbHandle);
1918 DBID tableID = { };
1919 DBPROPSET rgdbpRowSetPropSet[1];
1920 DBPROP rgdbpRowSetProp[1];
1921
1922 rgdbpRowSetPropSet[0].cProperties = 1;
1923 rgdbpRowSetPropSet[0].guidPropertySet = DBPROPSET_ROWSET;
1924 rgdbpRowSetPropSet[0].rgProperties = rgdbpRowSetProp;
1925
1926 rgdbpRowSetProp[0].dwPropertyID = DBPROP_IRowsetChange;
1927 rgdbpRowSetProp[0].dwOptions = DBPROPOPTIONS_REQUIRED;
1928 rgdbpRowSetProp[0].colid = DB_NULLID;
1929 rgdbpRowSetProp[0].vValue.vt = VT_BOOL;
1930 rgdbpRowSetProp[0].vValue.boolVal = VARIANT_TRUE;
1931
1932 // Finally, open all tables
1933 for (DWORD dwTable = 0; dwTable < pdsSchema->cTables; ++dwTable)
1934 {
1935 tableID.eKind = DBKIND_NAME;
1936 tableID.uName.pwszName = const_cast<WCHAR *>(pdsSchema->rgTables[dwTable].wzName);
1937
1938 // And finally, open the table's standard interfaces
1939 hr = pDatabaseInternal->pIOpenRowset->OpenRowset(NULL, &tableID, NULL, IID_IRowset, _countof(rgdbpRowSetPropSet), rgdbpRowSetPropSet, reinterpret_cast<IUnknown **>(&pdsSchema->rgTables[dwTable].pIRowset));
1940 ExitOnFailure(hr, "Failed to open table %u named %ls after ensuring all indexes and constraints are created", dwTable, pdsSchema->rgTables[dwTable].wzName);
1941
1942 hr = pdsSchema->rgTables[dwTable].pIRowset->QueryInterface(IID_IRowsetChange, reinterpret_cast<void **>(&pdsSchema->rgTables[dwTable].pIRowsetChange));
1943 ExitOnFailure(hr, "Failed to get IRowsetChange object for table: %ls", pdsSchema->rgTables[dwTable].wzName);
1944 }
1945
1946LExit:
1947 return hr;
1948}
1949
1950static HRESULT SetColumnValue(
1951 __in const SCE_TABLE_SCHEMA *pTableSchema,
1952 __in DWORD dwColumnIndex,
1953 __in_bcount_opt(cbSize) const BYTE *pbData,
1954 __in SIZE_T cbSize,
1955 __inout DBBINDING *pBinding,
1956 __inout SIZE_T *pcbOffset,
1957 __inout BYTE **ppbBuffer
1958 )
1959{
1960 HRESULT hr = S_OK;
1961 size_t cbNewOffset = *pcbOffset;
1962
1963 pBinding->iOrdinal = dwColumnIndex + 1; // Skip bookmark column
1964 pBinding->dwMemOwner = DBMEMOWNER_CLIENTOWNED;
1965 pBinding->dwPart = DBPART_VALUE | DBPART_LENGTH | DBPART_STATUS;
1966
1967 pBinding->obLength = cbNewOffset;
1968
1969 hr = ::SizeTAdd(cbNewOffset, sizeof(DBBYTEOFFSET), &cbNewOffset);
1970 ExitOnFailure(hr, "Failed to add sizeof(DBBYTEOFFSET) to alloc size while setting column value");
1971
1972 pBinding->obValue = cbNewOffset;
1973
1974 hr = ::SizeTAdd(cbNewOffset, cbSize, &cbNewOffset);
1975 ExitOnFailure(hr, "Failed to add %u to alloc size while setting column value", cbSize);
1976
1977 pBinding->obStatus = cbNewOffset;
1978 pBinding->eParamIO = DBPARAMIO_INPUT;
1979
1980 hr = ::SizeTAdd(cbNewOffset, sizeof(DBSTATUS), &cbNewOffset);
1981 ExitOnFailure(hr, "Failed to add sizeof(DBSTATUS) to alloc size while setting column value");
1982
1983 pBinding->wType = pTableSchema->rgColumns[dwColumnIndex].dbtColumnType;
1984 pBinding->cbMaxLen = static_cast<DBBYTEOFFSET>(cbSize);
1985
1986 if (NULL == *ppbBuffer)
1987 {
1988 *ppbBuffer = reinterpret_cast<BYTE *>(MemAlloc(cbNewOffset, TRUE));
1989 ExitOnNull(*ppbBuffer, hr, E_OUTOFMEMORY, "Failed to allocate buffer while setting row string");
1990 }
1991 else
1992 {
1993 *ppbBuffer = reinterpret_cast<BYTE *>(MemReAlloc(*ppbBuffer, cbNewOffset, TRUE));
1994 ExitOnNull(*ppbBuffer, hr, E_OUTOFMEMORY, "Failed to reallocate buffer while setting row string");
1995 }
1996
1997 *(reinterpret_cast<DBBYTEOFFSET *>(*ppbBuffer + *pcbOffset)) = static_cast<DBBYTEOFFSET>(cbSize);
1998 *pcbOffset += sizeof(DBBYTEOFFSET);
1999 memcpy(*ppbBuffer + *pcbOffset, pbData, cbSize);
2000 *pcbOffset += cbSize;
2001 if (NULL == pbData)
2002 {
2003 *(reinterpret_cast<DBSTATUS *>(*ppbBuffer + *pcbOffset)) = DBSTATUS_S_ISNULL;
2004 }
2005 *pcbOffset += sizeof(DBSTATUS);
2006
2007LExit:
2008 return hr;
2009}
2010
2011static HRESULT GetColumnValue(
2012 __in SCE_ROW *pRow,
2013 __in DWORD dwColumnIndex,
2014 __out_opt BYTE **ppbData,
2015 __out SIZE_T *pcbSize
2016 )
2017{
2018 HRESULT hr = S_OK;
2019 const SCE_TABLE_SCHEMA *pTable = pRow->pTableSchema;
2020 IAccessor *pIAccessor = NULL;
2021 HACCESSOR hAccessorLength = DB_NULL_HACCESSOR;
2022 HACCESSOR hAccessorValue = DB_NULL_HACCESSOR;
2023 DWORD dwDataSize = 0;
2024 void *pvRawData = NULL;
2025 DBBINDING dbBinding = { };
2026 DBBINDSTATUS dbBindStatus = DBBINDSTATUS_OK;
2027
2028 dbBinding.iOrdinal = dwColumnIndex + 1;
2029 dbBinding.dwMemOwner = DBMEMOWNER_CLIENTOWNED;
2030 dbBinding.dwPart = DBPART_LENGTH;
2031 dbBinding.wType = pTable->rgColumns[dwColumnIndex].dbtColumnType;
2032
2033 pRow->pIRowset->QueryInterface(IID_IAccessor, reinterpret_cast<void **>(&pIAccessor));
2034 ExitOnFailure(hr, "Failed to get IAccessor interface");
2035
2036 hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, &dbBinding, 0, &hAccessorLength, &dbBindStatus);
2037 ExitOnFailure(hr, "Failed to create accessor");
2038
2039 hr = pRow->pIRowset->GetData(pRow->hRow, hAccessorLength, reinterpret_cast<void *>(&dwDataSize));
2040 ExitOnFailure(hr, "Failed to get size of data");
2041
2042 // For variable-length columns, zero data returned means NULL
2043 if (0 == dwDataSize)
2044 {
2045 ExitFunction1(hr = E_NOTFOUND);
2046 }
2047
2048 if (NULL != ppbData)
2049 {
2050 dbBinding.dwPart = DBPART_VALUE;
2051 dbBinding.cbMaxLen = dwDataSize;
2052
2053 hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, &dbBinding, 0, &hAccessorValue, &dbBindStatus);
2054 ExitOnFailure(hr, "Failed to create accessor");
2055
2056 if (DBBINDSTATUS_OK != dbBindStatus)
2057 {
2058 hr = E_INVALIDARG;
2059 ExitOnFailure(hr, "Bad bind status while creating accessor to get value");
2060 }
2061
2062 if (DBTYPE_WSTR == dbBinding.wType)
2063 {
2064 hr = StrAlloc(reinterpret_cast<LPWSTR *>(&pvRawData), dwDataSize / sizeof(WCHAR));
2065 ExitOnFailure(hr, "Failed to allocate space for string data while reading column %u", dwColumnIndex);
2066 }
2067 else
2068 {
2069 pvRawData = MemAlloc(dwDataSize, TRUE);
2070 ExitOnNull(pvRawData, hr, E_OUTOFMEMORY, "Failed to allocate space for data while reading column %u", dwColumnIndex);
2071 }
2072
2073 hr = pRow->pIRowset->GetData(pRow->hRow, hAccessorValue, pvRawData);
2074 ExitOnFailure(hr, "Failed to read data value");
2075
2076 ReleaseMem(*ppbData);
2077 *ppbData = reinterpret_cast<BYTE *>(pvRawData);
2078 pvRawData = NULL;
2079 }
2080
2081 *pcbSize = dwDataSize;
2082
2083LExit:
2084 ReleaseMem(pvRawData);
2085
2086 if (DB_NULL_HACCESSOR != hAccessorLength)
2087 {
2088 pIAccessor->ReleaseAccessor(hAccessorLength, NULL);
2089 }
2090 if (DB_NULL_HACCESSOR != hAccessorValue)
2091 {
2092 pIAccessor->ReleaseAccessor(hAccessorValue, NULL);
2093 }
2094 ReleaseObject(pIAccessor);
2095
2096 return hr;
2097}
2098
2099static HRESULT GetColumnValueFixed(
2100 __in SCE_ROW *pRow,
2101 __in DWORD dwColumnIndex,
2102 __in DWORD cbSize,
2103 __out BYTE *pbData
2104 )
2105{
2106 HRESULT hr = S_OK;
2107 const SCE_TABLE_SCHEMA *pTable = pRow->pTableSchema;
2108 IAccessor *pIAccessor = NULL;
2109 HACCESSOR hAccessorLength = DB_NULL_HACCESSOR;
2110 HACCESSOR hAccessorValue = DB_NULL_HACCESSOR;
2111 DWORD dwDataSize = 0;
2112 DBBINDSTATUS dbBindStatus = DBBINDSTATUS_OK;
2113 DBBINDING dbBinding = { };
2114
2115 dbBinding.iOrdinal = dwColumnIndex + 1;
2116 dbBinding.dwMemOwner = DBMEMOWNER_CLIENTOWNED;
2117 dbBinding.dwPart = DBPART_LENGTH;
2118 dbBinding.wType = pTable->rgColumns[dwColumnIndex].dbtColumnType;
2119
2120 pRow->pIRowset->QueryInterface(IID_IAccessor, reinterpret_cast<void **>(&pIAccessor));
2121 ExitOnFailure(hr, "Failed to get IAccessor interface");
2122
2123 hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, &dbBinding, 0, &hAccessorLength, &dbBindStatus);
2124 ExitOnFailure(hr, "Failed to create accessor");
2125
2126 if (DBBINDSTATUS_OK != dbBindStatus)
2127 {
2128 hr = E_INVALIDARG;
2129 ExitOnFailure(hr, "Bad bind status while creating accessor to get length of value");
2130 }
2131
2132 hr = pRow->pIRowset->GetData(pRow->hRow, hAccessorLength, reinterpret_cast<void *>(&dwDataSize));
2133 ExitOnFailure(hr, "Failed to get size of data");
2134
2135 if (0 == dwDataSize)
2136 {
2137 ExitFunction1(hr = E_NOTFOUND);
2138 }
2139
2140 dbBinding.dwPart = DBPART_VALUE;
2141 dbBinding.cbMaxLen = cbSize;
2142
2143 hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, &dbBinding, 0, &hAccessorValue, &dbBindStatus);
2144 ExitOnFailure(hr, "Failed to create accessor");
2145
2146 if (DBBINDSTATUS_OK != dbBindStatus)
2147 {
2148 hr = E_INVALIDARG;
2149 ExitOnFailure(hr, "Bad bind status while creating accessor to get value");
2150 }
2151
2152 hr = pRow->pIRowset->GetData(pRow->hRow, hAccessorValue, reinterpret_cast<void *>(pbData));
2153 ExitOnFailure(hr, "Failed to read data value");
2154
2155LExit:
2156 if (DB_NULL_HACCESSOR != hAccessorLength)
2157 {
2158 pIAccessor->ReleaseAccessor(hAccessorLength, NULL);
2159 }
2160 if (DB_NULL_HACCESSOR != hAccessorValue)
2161 {
2162 pIAccessor->ReleaseAccessor(hAccessorValue, NULL);
2163 }
2164 ReleaseObject(pIAccessor);
2165
2166 return hr;
2167}
2168
2169static HRESULT EnsureLocalColumnConstraints(
2170 __in ITableDefinition *pTableDefinition,
2171 __in DBID *pTableID,
2172 __in SCE_TABLE_SCHEMA *pTableSchema
2173 )
2174{
2175 HRESULT hr = S_OK;
2176 SCE_COLUMN_SCHEMA *pCurrentColumn = NULL;
2177 DBCONSTRAINTDESC dbcdConstraint = { };
2178 DBID dbConstraintID = { };
2179 DBID dbLocalColumnID = { };
2180 ITableDefinitionWithConstraints *pTableDefinitionWithConstraints = NULL;
2181
2182 hr = pTableDefinition->QueryInterface(IID_ITableDefinitionWithConstraints, reinterpret_cast<void **>(&pTableDefinitionWithConstraints));
2183 ExitOnFailure(hr, "Failed to query for ITableDefinitionWithConstraints interface in order to create column constraints");
2184
2185 for (DWORD i = 0; i < pTableSchema->cColumns; ++i)
2186 {
2187 pCurrentColumn = pTableSchema->rgColumns + i;
2188
2189 // Add a primary key constraint for this column, if one exists
2190 if (pCurrentColumn->fPrimaryKey)
2191 {
2192 // Setup DBID for new constraint
2193 dbConstraintID.eKind = DBKIND_NAME;
2194 dbConstraintID.uName.pwszName = const_cast<LPOLESTR>(L"PrimaryKey");
2195 dbcdConstraint.pConstraintID = &dbConstraintID;
2196
2197 dbcdConstraint.ConstraintType = DBCONSTRAINTTYPE_PRIMARYKEY;
2198
2199 dbLocalColumnID.eKind = DBKIND_NAME;
2200 dbLocalColumnID.uName.pwszName = const_cast<LPOLESTR>(pCurrentColumn->wzName);
2201 dbcdConstraint.cColumns = 1;
2202 dbcdConstraint.rgColumnList = &dbLocalColumnID;
2203
2204 dbcdConstraint.pReferencedTableID = NULL;
2205 dbcdConstraint.cForeignKeyColumns = 0;
2206 dbcdConstraint.rgForeignKeyColumnList = NULL;
2207 dbcdConstraint.pwszConstraintText = NULL;
2208 dbcdConstraint.UpdateRule = DBUPDELRULE_NOACTION;
2209 dbcdConstraint.DeleteRule = DBUPDELRULE_NOACTION;
2210 dbcdConstraint.MatchType = DBMATCHTYPE_NONE;
2211 dbcdConstraint.Deferrability = 0;
2212 dbcdConstraint.cReserved = 0;
2213 dbcdConstraint.rgReserved = NULL;
2214
2215 hr = pTableDefinitionWithConstraints->AddConstraint(pTableID, &dbcdConstraint);
2216 if (DB_E_DUPLICATECONSTRAINTID == hr)
2217 {
2218 hr = S_OK;
2219 }
2220 ExitOnFailure(hr, "Failed to add primary key constraint for column %ls, table %ls", pCurrentColumn->wzName, pTableSchema->wzName);
2221 }
2222 }
2223
2224LExit:
2225 ReleaseObject(pTableDefinitionWithConstraints);
2226
2227 return hr;
2228}
2229
2230static HRESULT EnsureForeignColumnConstraints(
2231 __in ITableDefinition *pTableDefinition,
2232 __in DBID *pTableID,
2233 __in SCE_TABLE_SCHEMA *pTableSchema,
2234 __in SCE_DATABASE_SCHEMA *pDatabaseSchema
2235 )
2236{
2237 HRESULT hr = S_OK;
2238 SCE_COLUMN_SCHEMA *pCurrentColumn = NULL;
2239 DBCONSTRAINTDESC dbcdConstraint = { };
2240 DBID dbConstraintID = { };
2241 DBID dbLocalColumnID = { };
2242 DBID dbForeignTableID = { };
2243 DBID dbForeignColumnID = { };
2244 ITableDefinitionWithConstraints *pTableDefinitionWithConstraints = NULL;
2245
2246 hr = pTableDefinition->QueryInterface(IID_ITableDefinitionWithConstraints, reinterpret_cast<void **>(&pTableDefinitionWithConstraints));
2247 ExitOnFailure(hr, "Failed to query for ITableDefinitionWithConstraints interface in order to create column constraints");
2248
2249 for (DWORD i = 0; i < pTableSchema->cColumns; ++i)
2250 {
2251 pCurrentColumn = pTableSchema->rgColumns + i;
2252
2253 // Add a foreign key constraint for this column, if one exists
2254 if (NULL != pCurrentColumn->wzRelationName)
2255 {
2256 // Setup DBID for new constraint
2257 dbConstraintID.eKind = DBKIND_NAME;
2258 dbConstraintID.uName.pwszName = const_cast<LPOLESTR>(pCurrentColumn->wzRelationName);
2259 dbcdConstraint.pConstraintID = &dbConstraintID;
2260
2261 dbcdConstraint.ConstraintType = DBCONSTRAINTTYPE_FOREIGNKEY;
2262
2263 dbForeignColumnID.eKind = DBKIND_NAME;
2264 dbForeignColumnID.uName.pwszName = const_cast<LPOLESTR>(pDatabaseSchema->rgTables[pCurrentColumn->dwForeignKeyTable].rgColumns[pCurrentColumn->dwForeignKeyColumn].wzName);
2265 dbcdConstraint.cColumns = 1;
2266 dbcdConstraint.rgColumnList = &dbForeignColumnID;
2267
2268 dbForeignTableID.eKind = DBKIND_NAME;
2269 dbForeignTableID.uName.pwszName = const_cast<LPOLESTR>(pDatabaseSchema->rgTables[pCurrentColumn->dwForeignKeyTable].wzName);
2270 dbcdConstraint.pReferencedTableID = &dbForeignTableID;
2271
2272 dbLocalColumnID.eKind = DBKIND_NAME;
2273 dbLocalColumnID.uName.pwszName = const_cast<LPOLESTR>(pCurrentColumn->wzName);
2274 dbcdConstraint.cForeignKeyColumns = 1;
2275 dbcdConstraint.rgForeignKeyColumnList = &dbLocalColumnID;
2276
2277 dbcdConstraint.pwszConstraintText = NULL;
2278 dbcdConstraint.UpdateRule = DBUPDELRULE_NOACTION;
2279 dbcdConstraint.DeleteRule = DBUPDELRULE_NOACTION;
2280 dbcdConstraint.MatchType = DBMATCHTYPE_FULL;
2281 dbcdConstraint.Deferrability = 0;
2282 dbcdConstraint.cReserved = 0;
2283 dbcdConstraint.rgReserved = NULL;
2284
2285 hr = pTableDefinitionWithConstraints->AddConstraint(pTableID, &dbcdConstraint);
2286 if (DB_E_DUPLICATECONSTRAINTID == hr)
2287 {
2288 hr = S_OK;
2289 }
2290 ExitOnFailure(hr, "Failed to add constraint named: %ls to table: %ls", pCurrentColumn->wzRelationName, pTableSchema->wzName);
2291 }
2292 }
2293
2294LExit:
2295 ReleaseObject(pTableDefinitionWithConstraints);
2296
2297 return hr;
2298}
2299
2300static HRESULT SetSessionProperties(
2301 __in ISessionProperties *pISessionProperties
2302 )
2303{
2304 HRESULT hr = S_OK;
2305 DBPROP rgdbpDataSourceProp[1];
2306 DBPROPSET rgdbpDataSourcePropSet[1];
2307
2308 rgdbpDataSourceProp[0].dwPropertyID = DBPROP_SSCE_TRANSACTION_COMMIT_MODE;
2309 rgdbpDataSourceProp[0].dwOptions = DBPROPOPTIONS_REQUIRED;
2310 rgdbpDataSourceProp[0].vValue.vt = VT_I4;
2311 rgdbpDataSourceProp[0].vValue.lVal = DBPROPVAL_SSCE_TCM_FLUSH;
2312
2313 rgdbpDataSourcePropSet[0].guidPropertySet = DBPROPSET_SSCE_SESSION;
2314 rgdbpDataSourcePropSet[0].rgProperties = rgdbpDataSourceProp;
2315 rgdbpDataSourcePropSet[0].cProperties = 1;
2316
2317 hr = pISessionProperties->SetProperties(1, rgdbpDataSourcePropSet);
2318 ExitOnFailure(hr, "Failed to set session properties");
2319
2320LExit:
2321 return hr;
2322}
2323
2324static HRESULT GetDatabaseSchemaInfo(
2325 __in SCE_DATABASE *pDatabase,
2326 __out LPWSTR *psczSchemaType,
2327 __out DWORD *pdwVersion
2328 )
2329{
2330 HRESULT hr = S_OK;
2331 LPWSTR sczSchemaType = NULL;
2332 DWORD dwVersionFound = 0;
2333 SCE_TABLE_SCHEMA schemaTable = SCE_INTERNAL_VERSION_TABLE_SCHEMA[0];
2334 SCE_DATABASE_SCHEMA fullSchema = { 1, &schemaTable};
2335 // Database object with our alternate schema
2336 SCE_DATABASE database = { pDatabase->sdbHandle, &fullSchema };
2337 SCE_ROW_HANDLE sceRow = NULL;
2338
2339 hr = OpenSchema(pDatabase, &fullSchema);
2340 ExitOnFailure(hr, "Failed to ensure internal version schema");
2341
2342 hr = SceGetFirstRow(&database, 0, &sceRow);
2343 ExitOnFailure(hr, "Failed to get first row in internal version schema table");
2344
2345 hr = SceGetColumnString(sceRow, 0, &sczSchemaType);
2346 ExitOnFailure(hr, "Failed to get internal schematype");
2347
2348 hr = SceGetColumnDword(sceRow, 1, &dwVersionFound);
2349 ExitOnFailure(hr, "Failed to get internal version");
2350
2351 *psczSchemaType = sczSchemaType;
2352 sczSchemaType = NULL;
2353 *pdwVersion = dwVersionFound;
2354
2355LExit:
2356 SceCloseTable(&schemaTable); // ignore failure
2357 ReleaseStr(sczSchemaType);
2358 ReleaseSceRow(sceRow);
2359
2360 return hr;
2361}
2362
2363static HRESULT SetDatabaseSchemaInfo(
2364 __in SCE_DATABASE *pDatabase,
2365 __in LPCWSTR wzSchemaType,
2366 __in DWORD dwVersion
2367 )
2368{
2369 HRESULT hr = S_OK;
2370 BOOL fInSceTransaction = FALSE;
2371 SCE_TABLE_SCHEMA schemaTable = SCE_INTERNAL_VERSION_TABLE_SCHEMA[0];
2372 SCE_DATABASE_SCHEMA fullSchema = { 1, &schemaTable};
2373 // Database object with our alternate schema
2374 SCE_DATABASE database = { pDatabase->sdbHandle, &fullSchema };
2375 SCE_ROW_HANDLE sceRow = NULL;
2376
2377 hr = EnsureSchema(pDatabase, &fullSchema);
2378 ExitOnFailure(hr, "Failed to ensure internal version schema");
2379
2380 hr = SceBeginTransaction(&database);
2381 ExitOnFailure(hr, "Failed to begin transaction");
2382 fInSceTransaction = TRUE;
2383
2384 hr = SceGetFirstRow(&database, 0, &sceRow);
2385 if (E_NOTFOUND == hr)
2386 {
2387 hr = ScePrepareInsert(&database, 0, &sceRow);
2388 ExitOnFailure(hr, "Failed to insert only row into internal version schema table");
2389 }
2390 else
2391 {
2392 ExitOnFailure(hr, "Failed to get first row in internal version schema table");
2393 }
2394
2395 hr = SceSetColumnString(sceRow, 0, wzSchemaType);
2396 ExitOnFailure(hr, "Failed to set internal schematype to: %ls", wzSchemaType);
2397
2398 hr = SceSetColumnDword(sceRow, 1, dwVersion);
2399 ExitOnFailure(hr, "Failed to set internal version to: %u", dwVersion);
2400
2401 hr = SceFinishUpdate(sceRow);
2402 ExitOnFailure(hr, "Failed to insert first row in internal version schema table");
2403
2404 hr = SceCommitTransaction(&database);
2405 ExitOnFailure(hr, "Failed to commit transaction");
2406 fInSceTransaction = FALSE;
2407
2408LExit:
2409 SceCloseTable(&schemaTable); // ignore failure
2410 ReleaseSceRow(sceRow);
2411 if (fInSceTransaction)
2412 {
2413 SceRollbackTransaction(&database);
2414 }
2415
2416 return hr;
2417}
2418
2419static void ReleaseDatabase(
2420 SCE_DATABASE *pDatabase
2421 )
2422{
2423 if (NULL != pDatabase)
2424 {
2425 if (NULL != pDatabase->pdsSchema)
2426 {
2427 for (DWORD i = 0; i < pDatabase->pdsSchema->cTables; ++i)
2428 {
2429 SceCloseTable(pDatabase->pdsSchema->rgTables + i);
2430 }
2431 }
2432
2433 if (NULL != pDatabase->sdbHandle)
2434 {
2435 ReleaseDatabaseInternal(reinterpret_cast<SCE_DATABASE_INTERNAL *>(pDatabase->sdbHandle));
2436 }
2437 }
2438 ReleaseMem(pDatabase);
2439}
2440
2441static void ReleaseDatabaseInternal(
2442 SCE_DATABASE_INTERNAL *pDatabaseInternal
2443 )
2444{
2445 HRESULT hr = S_OK;
2446
2447 if (NULL != pDatabaseInternal)
2448 {
2449 ReleaseObject(pDatabaseInternal->pITransactionLocal);
2450 ReleaseObject(pDatabaseInternal->pIOpenRowset);
2451 ReleaseObject(pDatabaseInternal->pISessionProperties);
2452 ReleaseObject(pDatabaseInternal->pIDBCreateSession);
2453 ReleaseObject(pDatabaseInternal->pIDBProperties);
2454
2455 if (NULL != pDatabaseInternal->pIDBInitialize)
2456 {
2457 hr = pDatabaseInternal->pIDBInitialize->Uninitialize();
2458 if (FAILED(hr))
2459 {
2460 TraceError(hr, "Failed to call uninitialize on IDBInitialize");
2461 }
2462 ReleaseObject(pDatabaseInternal->pIDBInitialize);
2463 }
2464
2465 if (NULL != pDatabaseInternal->hSqlCeDll)
2466 {
2467 if (!::FreeLibrary(pDatabaseInternal->hSqlCeDll))
2468 {
2469 hr = HRESULT_FROM_WIN32(::GetLastError());
2470 TraceError(hr, "Failed to free sql ce dll");
2471 }
2472 }
2473 }
2474
2475 // If there was a temp file we copied to (for read-only databases), delete it after close
2476 if (NULL != pDatabaseInternal->sczTempDbFile)
2477 {
2478 hr = FileEnsureDelete(pDatabaseInternal->sczTempDbFile);
2479 if (FAILED(hr))
2480 {
2481 TraceError(hr, "Failed to delete temporary database file (copied here because the database was opened as read-only): %ls", pDatabaseInternal->sczTempDbFile);
2482 }
2483 ReleaseStr(pDatabaseInternal->sczTempDbFile);
2484 }
2485
2486 ReleaseMem(pDatabaseInternal);
2487}
2488
2489#endif // end SKIP_SCE_COMPILE
diff --git a/src/libs/dutil/WixToolset.DUtil/shelutil.cpp b/src/libs/dutil/WixToolset.DUtil/shelutil.cpp
new file mode 100644
index 00000000..2eb9a52a
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/shelutil.cpp
@@ -0,0 +1,342 @@
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// Exit macros
7#define ShelExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__)
8#define ShelExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__)
9#define ShelExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__)
10#define ShelExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__)
11#define ShelExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__)
12#define ShelExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__)
13#define ShelExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_SHELUTIL, p, x, e, s, __VA_ARGS__)
14#define ShelExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_SHELUTIL, p, x, s, __VA_ARGS__)
15#define ShelExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_SHELUTIL, p, x, e, s, __VA_ARGS__)
16#define ShelExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_SHELUTIL, p, x, s, __VA_ARGS__)
17#define ShelExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_SHELUTIL, e, x, s, __VA_ARGS__)
18#define ShelExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_SHELUTIL, g, x, s, __VA_ARGS__)
19
20static PFN_SHELLEXECUTEEXW vpfnShellExecuteExW = ::ShellExecuteExW;
21
22static HRESULT GetDesktopShellView(
23 __in REFIID riid,
24 __out void **ppv
25 );
26static HRESULT GetShellDispatchFromView(
27 __in IShellView *psv,
28 __in REFIID riid,
29 __out void **ppv
30 );
31
32/********************************************************************
33 ShelFunctionOverride - overrides the shell functions. Typically used
34 for unit testing.
35
36*********************************************************************/
37extern "C" void DAPI ShelFunctionOverride(
38 __in_opt PFN_SHELLEXECUTEEXW pfnShellExecuteExW
39 )
40{
41 vpfnShellExecuteExW = pfnShellExecuteExW ? pfnShellExecuteExW : ::ShellExecuteExW;
42}
43
44
45/********************************************************************
46 ShelExec() - executes a target.
47
48*******************************************************************/
49extern "C" HRESULT DAPI ShelExec(
50 __in_z LPCWSTR wzTargetPath,
51 __in_z_opt LPCWSTR wzParameters,
52 __in_z_opt LPCWSTR wzVerb,
53 __in_z_opt LPCWSTR wzWorkingDirectory,
54 __in int nShowCmd,
55 __in_opt HWND hwndParent,
56 __out_opt HANDLE* phProcess
57 )
58{
59 HRESULT hr = S_OK;
60 SHELLEXECUTEINFOW shExecInfo = {};
61
62 shExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
63 shExecInfo.fMask = SEE_MASK_FLAG_DDEWAIT | SEE_MASK_FLAG_NO_UI | SEE_MASK_NOCLOSEPROCESS;
64 shExecInfo.hwnd = hwndParent;
65 shExecInfo.lpVerb = wzVerb;
66 shExecInfo.lpFile = wzTargetPath;
67 shExecInfo.lpParameters = wzParameters;
68 shExecInfo.lpDirectory = wzWorkingDirectory;
69 shExecInfo.nShow = nShowCmd;
70
71 if (!vpfnShellExecuteExW(&shExecInfo))
72 {
73 ShelExitWithLastError(hr, "ShellExecEx failed with return code: %d", Dutil_er);
74 }
75
76 if (phProcess)
77 {
78 *phProcess = shExecInfo.hProcess;
79 shExecInfo.hProcess = NULL;
80 }
81
82LExit:
83 ReleaseHandle(shExecInfo.hProcess);
84
85 return hr;
86}
87
88
89/********************************************************************
90 ShelExecUnelevated() - executes a target unelevated.
91
92*******************************************************************/
93extern "C" HRESULT DAPI ShelExecUnelevated(
94 __in_z LPCWSTR wzTargetPath,
95 __in_z_opt LPCWSTR wzParameters,
96 __in_z_opt LPCWSTR wzVerb,
97 __in_z_opt LPCWSTR wzWorkingDirectory,
98 __in int nShowCmd
99 )
100{
101 HRESULT hr = S_OK;
102 BSTR bstrTargetPath = NULL;
103 VARIANT vtParameters = { };
104 VARIANT vtVerb = { };
105 VARIANT vtWorkingDirectory = { };
106 VARIANT vtShow = { };
107 IShellView* psv = NULL;
108 IShellDispatch2* psd = NULL;
109
110 bstrTargetPath = ::SysAllocString(wzTargetPath);
111 ShelExitOnNull(bstrTargetPath, hr, E_OUTOFMEMORY, "Failed to allocate target path BSTR.");
112
113 if (wzParameters && *wzParameters)
114 {
115 vtParameters.vt = VT_BSTR;
116 vtParameters.bstrVal = ::SysAllocString(wzParameters);
117 ShelExitOnNull(bstrTargetPath, hr, E_OUTOFMEMORY, "Failed to allocate parameters BSTR.");
118 }
119
120 if (wzVerb && *wzVerb)
121 {
122 vtVerb.vt = VT_BSTR;
123 vtVerb.bstrVal = ::SysAllocString(wzVerb);
124 ShelExitOnNull(bstrTargetPath, hr, E_OUTOFMEMORY, "Failed to allocate verb BSTR.");
125 }
126
127 if (wzWorkingDirectory && *wzWorkingDirectory)
128 {
129 vtWorkingDirectory.vt = VT_BSTR;
130 vtWorkingDirectory.bstrVal = ::SysAllocString(wzWorkingDirectory);
131 ShelExitOnNull(bstrTargetPath, hr, E_OUTOFMEMORY, "Failed to allocate working directory BSTR.");
132 }
133
134 vtShow.vt = VT_INT;
135 vtShow.intVal = nShowCmd;
136
137 hr = GetDesktopShellView(IID_PPV_ARGS(&psv));
138 ShelExitOnFailure(hr, "Failed to get desktop shell view.");
139
140 hr = GetShellDispatchFromView(psv, IID_PPV_ARGS(&psd));
141 ShelExitOnFailure(hr, "Failed to get shell dispatch from view.");
142
143 hr = psd->ShellExecute(bstrTargetPath, vtParameters, vtWorkingDirectory, vtVerb, vtShow);
144 if (S_FALSE == hr)
145 {
146 hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
147 }
148 ShelExitOnRootFailure(hr, "Failed to launch unelevate executable: %ls", bstrTargetPath);
149
150LExit:
151 ReleaseObject(psd);
152 ReleaseObject(psv);
153 ReleaseBSTR(vtWorkingDirectory.bstrVal);
154 ReleaseBSTR(vtVerb.bstrVal);
155 ReleaseBSTR(vtParameters.bstrVal);
156 ReleaseBSTR(bstrTargetPath);
157
158 return hr;
159}
160
161
162/********************************************************************
163 ShelGetFolder() - gets a folder by CSIDL.
164
165*******************************************************************/
166extern "C" HRESULT DAPI ShelGetFolder(
167 __out_z LPWSTR* psczFolderPath,
168 __in int csidlFolder
169 )
170{
171 HRESULT hr = S_OK;
172 WCHAR wzPath[MAX_PATH];
173
174 hr = ::SHGetFolderPathW(NULL, csidlFolder | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, wzPath);
175 ShelExitOnFailure(hr, "Failed to get folder path for CSIDL: %d", csidlFolder);
176
177 hr = StrAllocString(psczFolderPath, wzPath, 0);
178 ShelExitOnFailure(hr, "Failed to copy shell folder path: %ls", wzPath);
179
180 hr = PathBackslashTerminate(psczFolderPath);
181 ShelExitOnFailure(hr, "Failed to backslash terminate shell folder path: %ls", *psczFolderPath);
182
183LExit:
184 return hr;
185}
186
187
188/********************************************************************
189 ShelGetKnownFolder() - gets a folder by KNOWNFOLDERID.
190
191 Note: return E_NOTIMPL if called on pre-Vista operating systems.
192*******************************************************************/
193#ifndef REFKNOWNFOLDERID
194#define REFKNOWNFOLDERID REFGUID
195#endif
196
197#ifndef KF_FLAG_CREATE
198#define KF_FLAG_CREATE 0x00008000 // Make sure that the folder already exists or create it and apply security specified in folder definition
199#endif
200
201EXTERN_C typedef HRESULT (STDAPICALLTYPE *PFN_SHGetKnownFolderPath)(
202 REFKNOWNFOLDERID rfid,
203 DWORD dwFlags,
204 HANDLE hToken,
205 PWSTR *ppszPath
206 );
207
208extern "C" HRESULT DAPI ShelGetKnownFolder(
209 __out_z LPWSTR* psczFolderPath,
210 __in REFKNOWNFOLDERID rfidFolder
211 )
212{
213 HRESULT hr = S_OK;
214 HMODULE hShell32Dll = NULL;
215 PFN_SHGetKnownFolderPath pfn = NULL;
216 LPWSTR pwzPath = NULL;
217
218 hr = LoadSystemLibrary(L"shell32.dll", &hShell32Dll);
219 if (E_MODNOTFOUND == hr)
220 {
221 TraceError(hr, "Failed to load shell32.dll");
222 ExitFunction1(hr = E_NOTIMPL);
223 }
224 ShelExitOnFailure(hr, "Failed to load shell32.dll.");
225
226 pfn = reinterpret_cast<PFN_SHGetKnownFolderPath>(::GetProcAddress(hShell32Dll, "SHGetKnownFolderPath"));
227 ShelExitOnNull(pfn, hr, E_NOTIMPL, "Failed to find SHGetKnownFolderPath entry point.");
228
229 hr = pfn(rfidFolder, KF_FLAG_CREATE, NULL, &pwzPath);
230 ShelExitOnFailure(hr, "Failed to get known folder path.");
231
232 hr = StrAllocString(psczFolderPath, pwzPath, 0);
233 ShelExitOnFailure(hr, "Failed to copy shell folder path: %ls", pwzPath);
234
235 hr = PathBackslashTerminate(psczFolderPath);
236 ShelExitOnFailure(hr, "Failed to backslash terminate shell folder path: %ls", *psczFolderPath);
237
238LExit:
239 if (pwzPath)
240 {
241 ::CoTaskMemFree(pwzPath);
242 }
243
244 if (hShell32Dll)
245 {
246 ::FreeLibrary(hShell32Dll);
247 }
248
249 return hr;
250}
251
252
253// Internal functions.
254
255static HRESULT GetDesktopShellView(
256 __in REFIID riid,
257 __out void **ppv
258 )
259{
260 HRESULT hr = S_OK;
261 IShellWindows* psw = NULL;
262 HWND hwnd = NULL;
263 IDispatch* pdisp = NULL;
264 VARIANT vEmpty = {}; // VT_EMPTY
265 IShellBrowser* psb = NULL;
266 IShellFolder* psf = NULL;
267 IShellView* psv = NULL;
268
269 // use the shell view for the desktop using the shell windows automation to find the
270 // desktop web browser and then grabs its view
271 // returns IShellView, IFolderView and related interfaces
272 hr = ::CoCreateInstance(CLSID_ShellWindows, NULL, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&psw));
273 ShelExitOnFailure(hr, "Failed to get shell view.");
274
275 hr = psw->FindWindowSW(&vEmpty, &vEmpty, SWC_DESKTOP, (long*)&hwnd, SWFO_NEEDDISPATCH, &pdisp);
276 if (S_OK == hr)
277 {
278 hr = IUnknown_QueryService(pdisp, SID_STopLevelBrowser, IID_PPV_ARGS(&psb));
279 ShelExitOnFailure(hr, "Failed to get desktop window.");
280
281 hr = psb->QueryActiveShellView(&psv);
282 ShelExitOnFailure(hr, "Failed to get active shell view.");
283
284 hr = psv->QueryInterface(riid, ppv);
285 ShelExitOnFailure(hr, "Failed to query for the desktop shell view.");
286 }
287 else if (S_FALSE == hr)
288 {
289 //Windows XP
290 hr = SHGetDesktopFolder(&psf);
291 ShelExitOnFailure(hr, "Failed to get desktop folder.");
292
293 hr = psf->CreateViewObject(NULL, IID_IShellView, ppv);
294 ShelExitOnFailure(hr, "Failed to query for the desktop shell view.");
295 }
296 else
297 {
298 ShelExitOnFailure(hr, "Failed to get desktop window.");
299 }
300
301LExit:
302 ReleaseObject(psv);
303 ReleaseObject(psb);
304 ReleaseObject(psf);
305 ReleaseObject(pdisp);
306 ReleaseObject(psw);
307
308 return hr;
309}
310
311static HRESULT GetShellDispatchFromView(
312 __in IShellView *psv,
313 __in REFIID riid,
314 __out void **ppv
315 )
316{
317 HRESULT hr = S_OK;
318 IDispatch *pdispBackground = NULL;
319 IShellFolderViewDual *psfvd = NULL;
320 IDispatch *pdisp = NULL;
321
322 // From a shell view object, gets its automation interface and from that get the shell
323 // application object that implements IShellDispatch2 and related interfaces.
324 hr = psv->GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARGS(&pdispBackground));
325 ShelExitOnFailure(hr, "Failed to get the automation interface for shell.");
326
327 hr = pdispBackground->QueryInterface(IID_PPV_ARGS(&psfvd));
328 ShelExitOnFailure(hr, "Failed to get shell folder view dual.");
329
330 hr = psfvd->get_Application(&pdisp);
331 ShelExitOnFailure(hr, "Failed to application object.");
332
333 hr = pdisp->QueryInterface(riid, ppv);
334 ShelExitOnFailure(hr, "Failed to get IShellDispatch2.");
335
336LExit:
337 ReleaseObject(pdisp);
338 ReleaseObject(psfvd);
339 ReleaseObject(pdispBackground);
340
341 return hr;
342}
diff --git a/src/libs/dutil/WixToolset.DUtil/sqlutil.cpp b/src/libs/dutil/WixToolset.DUtil/sqlutil.cpp
new file mode 100644
index 00000000..782c7088
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/sqlutil.cpp
@@ -0,0 +1,1002 @@
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// okay, this may look a little weird, but sqlutil.h cannot be in the
6// pre-compiled header because we need to #define these things so the
7// correct GUID's get pulled into this object file
8#include <initguid.h>
9#define DBINITCONSTANTS
10#include "sqlutil.h"
11
12
13//Please note that only SQL native client 11 has TLS1.2 support
14#define _SQLNCLI_OLEDB_DEPRECATE_WARNING
15
16#if !defined(SQLNCLI_VER)
17#define SQLNCLI_VER 1100
18#endif
19
20#if SQLNCLI_VER >= 1100
21#if defined(_SQLNCLI_OLEDB_) || !defined(_SQLNCLI_ODBC_)
22#define SQLNCLI_CLSID CLSID_SQLNCLI11
23#endif // defined(_SQLNCLI_OLEDB_) || !defined(_SQLNCLI_ODBC_)
24extern const GUID OLEDBDECLSPEC _SQLNCLI_OLEDB_DEPRECATE_WARNING CLSID_SQLNCLI11 = { 0x397C2819L,0x8272,0x4532,{ 0xAD,0x3A,0xFB,0x5E,0x43,0xBE,0xAA,0x39 } };
25#endif // SQLNCLI_VER >= 1100
26
27// Exit macros
28#define SqlExitTrace(x, s, ...) ExitTraceSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__)
29#define SqlExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__)
30#define SqlExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__)
31#define SqlExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__)
32#define SqlExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__)
33#define SqlExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__)
34#define SqlExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__)
35#define SqlExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_SQLUTIL, p, x, e, s, __VA_ARGS__)
36#define SqlExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_SQLUTIL, p, x, s, __VA_ARGS__)
37#define SqlExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_SQLUTIL, p, x, e, s, __VA_ARGS__)
38#define SqlExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_SQLUTIL, p, x, s, __VA_ARGS__)
39#define SqlExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_SQLUTIL, e, x, s, __VA_ARGS__)
40#define SqlExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_SQLUTIL, g, x, s, __VA_ARGS__)
41
42// private prototypes
43static HRESULT InitializeDatabaseConnection(
44 __in REFCLSID rclsid,
45 __in_z LPCSTR szFriendlyClsidName,
46 __in DBPROPSET rgdbpsetInit[],
47 __in_ecount(rgdbpsetInit) DWORD cdbpsetInit,
48 __out IDBCreateSession** ppidbSession
49 );
50HRESULT DumpErrorRecords();
51static HRESULT FileSpecToString(
52 __in const SQL_FILESPEC* psf,
53 __out LPWSTR* ppwz
54 );
55static HRESULT EscapeSqlIdentifier(
56 __in_z LPCWSTR wzDatabase,
57 __deref_out_z LPWSTR* ppwz
58 );
59
60
61/********************************************************************
62 SqlConnectDatabase - establishes a connection to a database
63
64 NOTE: wzInstance is optional
65 if fIntegratedAuth is set then wzUser and wzPassword are ignored
66********************************************************************/
67extern "C" HRESULT DAPI SqlConnectDatabase(
68 __in_z LPCWSTR wzServer,
69 __in_z LPCWSTR wzInstance,
70 __in_z LPCWSTR wzDatabase,
71 __in BOOL fIntegratedAuth,
72 __in_z LPCWSTR wzUser,
73 __in_z LPCWSTR wzPassword,
74 __out IDBCreateSession** ppidbSession
75 )
76{
77 Assert(wzServer && wzDatabase && *wzDatabase && ppidbSession);
78
79 HRESULT hr = S_OK;
80 LPWSTR pwzServerInstance = NULL;
81 DBPROP rgdbpInit[4] = { };
82 DBPROPSET rgdbpsetInit[1] = { };
83 ULONG cProperties = 0;
84
85 // if there is an instance
86 if (wzInstance && *wzInstance)
87 {
88 hr = StrAllocFormatted(&pwzServerInstance, L"%s\\%s", wzServer, wzInstance);
89 }
90 else
91 {
92 hr = StrAllocString(&pwzServerInstance, wzServer, 0);
93 }
94 SqlExitOnFailure(hr, "failed to allocate memory for the server instance");
95
96 // server[\instance]
97 rgdbpInit[cProperties].dwPropertyID = DBPROP_INIT_DATASOURCE;
98 rgdbpInit[cProperties].dwOptions = DBPROPOPTIONS_REQUIRED;
99 rgdbpInit[cProperties].colid = DB_NULLID;
100 ::VariantInit(&rgdbpInit[cProperties].vValue);
101 rgdbpInit[cProperties].vValue.vt = VT_BSTR;
102 rgdbpInit[cProperties].vValue.bstrVal = ::SysAllocString(pwzServerInstance);
103 ++cProperties;
104
105 // database
106 rgdbpInit[cProperties].dwPropertyID = DBPROP_INIT_CATALOG;
107 rgdbpInit[cProperties].dwOptions = DBPROPOPTIONS_REQUIRED;
108 rgdbpInit[cProperties].colid = DB_NULLID;
109 ::VariantInit(&rgdbpInit[cProperties].vValue);
110 rgdbpInit[cProperties].vValue.vt = VT_BSTR;
111 rgdbpInit[cProperties].vValue.bstrVal= ::SysAllocString(wzDatabase);
112 ++cProperties;
113
114 if (fIntegratedAuth)
115 {
116 // username
117 rgdbpInit[cProperties].dwPropertyID = DBPROP_AUTH_INTEGRATED;
118 rgdbpInit[cProperties].dwOptions = DBPROPOPTIONS_REQUIRED;
119 rgdbpInit[cProperties].colid = DB_NULLID;
120 ::VariantInit(&rgdbpInit[cProperties].vValue);
121 rgdbpInit[cProperties].vValue.vt = VT_BSTR;
122 rgdbpInit[cProperties].vValue.bstrVal = ::SysAllocString(L"SSPI"); // default windows authentication
123 ++cProperties;
124 }
125 else
126 {
127 // username
128 rgdbpInit[cProperties].dwPropertyID = DBPROP_AUTH_USERID;
129 rgdbpInit[cProperties].dwOptions = DBPROPOPTIONS_REQUIRED;
130 rgdbpInit[cProperties].colid = DB_NULLID;
131 ::VariantInit(&rgdbpInit[cProperties].vValue);
132 rgdbpInit[cProperties].vValue.vt = VT_BSTR;
133 rgdbpInit[cProperties].vValue.bstrVal = ::SysAllocString(wzUser);
134 ++cProperties;
135
136 // password
137 rgdbpInit[cProperties].dwPropertyID = DBPROP_AUTH_PASSWORD;
138 rgdbpInit[cProperties].dwOptions = DBPROPOPTIONS_REQUIRED;
139 rgdbpInit[cProperties].colid = DB_NULLID;
140 ::VariantInit(&rgdbpInit[cProperties].vValue);
141 rgdbpInit[cProperties].vValue.vt = VT_BSTR;
142 rgdbpInit[cProperties].vValue.bstrVal = ::SysAllocString(wzPassword);
143 ++cProperties;
144 }
145
146 // put the properties into a set
147 rgdbpsetInit[0].guidPropertySet = DBPROPSET_DBINIT;
148 rgdbpsetInit[0].rgProperties = rgdbpInit;
149 rgdbpsetInit[0].cProperties = cProperties;
150
151 // obtain access to the SQL Native Client provider
152 hr = InitializeDatabaseConnection(SQLNCLI_CLSID, "SQL Native Client", rgdbpsetInit, countof(rgdbpsetInit), ppidbSession);
153 if (FAILED(hr))
154 {
155 SqlExitTrace(hr, "Could not initialize SQL Native Client, falling back to SQL OLE DB...");
156
157 // try OLE DB but if that fails return original error failure
158 HRESULT hr2 = InitializeDatabaseConnection(CLSID_SQLOLEDB, "SQL OLE DB", rgdbpsetInit, countof(rgdbpsetInit), ppidbSession);
159 if (FAILED(hr2))
160 {
161 SqlExitTrace(hr2, "Could not initialize SQL OLE DB either, giving up.");
162 }
163 else
164 {
165 hr = S_OK;
166 }
167 }
168
169LExit:
170 for (; 0 < cProperties; cProperties--)
171 {
172 ::VariantClear(&rgdbpInit[cProperties - 1].vValue);
173 }
174
175 ReleaseStr(pwzServerInstance);
176
177 return hr;
178}
179
180
181/********************************************************************
182 SqlStartTransaction - Starts a new transaction that must be ended
183
184*********************************************************************/
185extern "C" HRESULT DAPI SqlStartTransaction(
186 __in IDBCreateSession* pidbSession,
187 __out IDBCreateCommand** ppidbCommand,
188 __out ITransaction** ppit
189 )
190{
191 Assert(pidbSession && ppit);
192
193 HRESULT hr = S_OK;
194
195 hr = pidbSession->CreateSession(NULL, IID_IDBCreateCommand, (IUnknown**)ppidbCommand);
196 SqlExitOnFailure(hr, "unable to create command from session");
197
198 hr = (*ppidbCommand)->QueryInterface(IID_ITransactionLocal, (LPVOID*)ppit);
199 SqlExitOnFailure(hr, "Unable to QueryInterface session to get ITransactionLocal");
200
201 hr = ((ITransactionLocal*)*ppit)->StartTransaction(ISOLATIONLEVEL_SERIALIZABLE, 0, NULL, NULL);
202
203LExit:
204
205 return hr;
206}
207
208/********************************************************************
209 SqlEndTransaction - Ends the transaction
210
211 NOTE: if fCommit, will commit the transaction, otherwise rolls back
212*********************************************************************/
213extern "C" HRESULT DAPI SqlEndTransaction(
214 __in ITransaction* pit,
215 __in BOOL fCommit
216 )
217{
218 Assert(pit);
219
220 HRESULT hr = S_OK;
221
222 if (fCommit)
223 {
224 hr = pit->Commit(FALSE, XACTTC_SYNC, 0);
225 SqlExitOnFailure(hr, "commit of transaction failed");
226 }
227 else
228 {
229 hr = pit->Abort(NULL, FALSE, FALSE);
230 SqlExitOnFailure(hr, "abort of transaction failed");
231 }
232
233LExit:
234
235 return hr;
236}
237
238
239/********************************************************************
240 SqlDatabaseExists - determines if database exists
241
242 NOTE: wzInstance is optional
243 if fIntegratedAuth is set then wzUser and wzPassword are ignored
244 returns S_OK if database exist
245 returns S_FALSE if database does not exist
246 returns E_* on error
247********************************************************************/
248extern "C" HRESULT DAPI SqlDatabaseExists(
249 __in_z LPCWSTR wzServer,
250 __in_z LPCWSTR wzInstance,
251 __in_z LPCWSTR wzDatabase,
252 __in BOOL fIntegratedAuth,
253 __in_z LPCWSTR wzUser,
254 __in_z LPCWSTR wzPassword,
255 __out_opt BSTR* pbstrErrorDescription
256 )
257{
258 Assert(wzServer && wzDatabase && *wzDatabase);
259
260 HRESULT hr = S_OK;
261 IDBCreateSession* pidbSession = NULL;
262
263 hr = SqlConnectDatabase(wzServer, wzInstance, L"master", fIntegratedAuth, wzUser, wzPassword, &pidbSession);
264 SqlExitOnFailure(hr, "failed to connect to 'master' database on server %ls", wzServer);
265
266 hr = SqlSessionDatabaseExists(pidbSession, wzDatabase, pbstrErrorDescription);
267
268LExit:
269 ReleaseObject(pidbSession);
270
271 return hr;
272}
273
274
275/********************************************************************
276 SqlSessionDatabaseExists - determines if database exists
277
278 NOTE: pidbSession must be connected to master database
279 returns S_OK if database exist
280 returns S_FALSE if database does not exist
281 returns E_* on error
282********************************************************************/
283extern "C" HRESULT DAPI SqlSessionDatabaseExists(
284 __in IDBCreateSession* pidbSession,
285 __in_z LPCWSTR wzDatabase,
286 __out_opt BSTR* pbstrErrorDescription
287 )
288{
289 Assert(pidbSession && wzDatabase && *wzDatabase);
290
291 HRESULT hr = S_OK;
292
293 LPWSTR pwzQuery = NULL;
294 IRowset* pirs = NULL;
295
296 DBCOUNTITEM cRows = 0;
297 HROW rghRows[1];
298 HROW* prow = rghRows;
299
300 //
301 // query to see if the database exists
302 //
303 hr = StrAllocFormatted(&pwzQuery, L"SELECT name FROM sysdatabases WHERE name='%s'", wzDatabase);
304 SqlExitOnFailure(hr, "failed to allocate query string to ensure database exists");
305
306 hr = SqlSessionExecuteQuery(pidbSession, pwzQuery, &pirs, NULL, pbstrErrorDescription);
307 SqlExitOnFailure(hr, "failed to get database list from 'master' database");
308 Assert(pirs);
309
310 //
311 // check to see if the database was returned
312 //
313 hr = pirs->GetNextRows(DB_NULL_HCHAPTER, 0, 1, &cRows, &prow);
314 SqlExitOnFailure(hr, "failed to get row with database name");
315
316 // succeeded but no database
317 if ((DB_S_ENDOFROWSET == hr) || (0 == cRows))
318 {
319 hr = S_FALSE;
320 }
321
322LExit:
323 ReleaseObject(pirs);
324 ReleaseStr(pwzQuery);
325
326 return hr;
327}
328
329
330/********************************************************************
331 SqlDatabaseEnsureExists - creates a database if it does not exist
332
333 NOTE: wzInstance is optional
334 if fIntegratedAuth is set then wzUser and wzPassword are ignored
335********************************************************************/
336extern "C" HRESULT DAPI SqlDatabaseEnsureExists(
337 __in_z LPCWSTR wzServer,
338 __in_z LPCWSTR wzInstance,
339 __in_z LPCWSTR wzDatabase,
340 __in BOOL fIntegratedAuth,
341 __in_z LPCWSTR wzUser,
342 __in_z LPCWSTR wzPassword,
343 __in_opt const SQL_FILESPEC* psfDatabase,
344 __in_opt const SQL_FILESPEC* psfLog,
345 __out_opt BSTR* pbstrErrorDescription
346 )
347{
348 Assert(wzServer && wzDatabase && *wzDatabase);
349
350 HRESULT hr = S_OK;
351 IDBCreateSession* pidbSession = NULL;
352
353 //
354 // connect to the master database to create the new database
355 //
356 hr = SqlConnectDatabase(wzServer, wzInstance, L"master", fIntegratedAuth, wzUser, wzPassword, &pidbSession);
357 SqlExitOnFailure(hr, "failed to connect to 'master' database on server %ls", wzServer);
358
359 hr = SqlSessionDatabaseEnsureExists(pidbSession, wzDatabase, psfDatabase, psfLog, pbstrErrorDescription);
360 SqlExitOnFailure(hr, "failed to create database: %ls", wzDatabase);
361
362 Assert(S_OK == hr);
363LExit:
364 ReleaseObject(pidbSession);
365
366 return hr;
367}
368
369
370/********************************************************************
371 SqlSessionDatabaseEnsureExists - creates a database if it does not exist
372
373 NOTE: pidbSession must be connected to the master database
374********************************************************************/
375extern "C" HRESULT DAPI SqlSessionDatabaseEnsureExists(
376 __in IDBCreateSession* pidbSession,
377 __in_z LPCWSTR wzDatabase,
378 __in_opt const SQL_FILESPEC* psfDatabase,
379 __in_opt const SQL_FILESPEC* psfLog,
380 __out_opt BSTR* pbstrErrorDescription
381 )
382{
383 Assert(pidbSession && wzDatabase && *wzDatabase);
384
385 HRESULT hr = S_OK;
386
387 hr = SqlSessionDatabaseExists(pidbSession, wzDatabase, pbstrErrorDescription);
388 SqlExitOnFailure(hr, "failed to determine if exists, database: %ls", wzDatabase);
389
390 if (S_FALSE == hr)
391 {
392 hr = SqlSessionCreateDatabase(pidbSession, wzDatabase, psfDatabase, psfLog, pbstrErrorDescription);
393 SqlExitOnFailure(hr, "failed to create database: %ls", wzDatabase);
394 }
395 // else database already exists, return S_FALSE
396
397 Assert(S_OK == hr);
398LExit:
399
400 return hr;
401}
402
403
404/********************************************************************
405 SqlCreateDatabase - creates a database on the server
406
407 NOTE: wzInstance is optional
408 if fIntegratedAuth is set then wzUser and wzPassword are ignored
409********************************************************************/
410extern "C" HRESULT DAPI SqlCreateDatabase(
411 __in_z LPCWSTR wzServer,
412 __in_z LPCWSTR wzInstance,
413 __in_z LPCWSTR wzDatabase,
414 __in BOOL fIntegratedAuth,
415 __in_z LPCWSTR wzUser,
416 __in_z LPCWSTR wzPassword,
417 __in_opt const SQL_FILESPEC* psfDatabase,
418 __in_opt const SQL_FILESPEC* psfLog,
419 __out_opt BSTR* pbstrErrorDescription
420 )
421{
422 Assert(wzServer && wzDatabase && *wzDatabase);
423
424 HRESULT hr = S_OK;
425 IDBCreateSession* pidbSession = NULL;
426
427 //
428 // connect to the master database to create the new database
429 //
430 hr = SqlConnectDatabase(wzServer, wzInstance, L"master", fIntegratedAuth, wzUser, wzPassword, &pidbSession);
431 SqlExitOnFailure(hr, "failed to connect to 'master' database on server %ls", wzServer);
432
433 hr = SqlSessionCreateDatabase(pidbSession, wzDatabase, psfDatabase, psfLog, pbstrErrorDescription);
434 SqlExitOnFailure(hr, "failed to create database: %ls", wzDatabase);
435
436 Assert(S_OK == hr);
437LExit:
438 ReleaseObject(pidbSession);
439
440 return hr;
441}
442
443
444/********************************************************************
445 SqlSessionCreateDatabase - creates a database on the server
446
447 NOTE: pidbSession must be connected to the master database
448********************************************************************/
449extern "C" HRESULT DAPI SqlSessionCreateDatabase(
450 __in IDBCreateSession* pidbSession,
451 __in_z LPCWSTR wzDatabase,
452 __in_opt const SQL_FILESPEC* psfDatabase,
453 __in_opt const SQL_FILESPEC* psfLog,
454 __out_opt BSTR* pbstrErrorDescription
455 )
456{
457 HRESULT hr = S_OK;
458 LPWSTR pwzDbFile = NULL;
459 LPWSTR pwzLogFile = NULL;
460 LPWSTR pwzQuery = NULL;
461 LPWSTR pwzDatabaseEscaped = NULL;
462
463 if (psfDatabase)
464 {
465 hr = FileSpecToString(psfDatabase, &pwzDbFile);
466 SqlExitOnFailure(hr, "failed to convert db filespec to string");
467 }
468
469 if (psfLog)
470 {
471 hr = FileSpecToString(psfLog, &pwzLogFile);
472 SqlExitOnFailure(hr, "failed to convert log filespec to string");
473 }
474
475 hr = EscapeSqlIdentifier(wzDatabase, &pwzDatabaseEscaped);
476 SqlExitOnFailure(hr, "failed to escape database string");
477
478 hr = StrAllocFormatted(&pwzQuery, L"CREATE DATABASE %s %s%s %s%s", pwzDatabaseEscaped, pwzDbFile ? L"ON " : L"", pwzDbFile ? pwzDbFile : L"", pwzLogFile ? L"LOG ON " : L"", pwzLogFile ? pwzLogFile : L"");
479 SqlExitOnFailure(hr, "failed to allocate query to create database: %ls", pwzDatabaseEscaped);
480
481 hr = SqlSessionExecuteQuery(pidbSession, pwzQuery, NULL, NULL, pbstrErrorDescription);
482 SqlExitOnFailure(hr, "failed to create database: %ls, Query: %ls", pwzDatabaseEscaped, pwzQuery);
483
484LExit:
485 ReleaseStr(pwzQuery);
486 ReleaseStr(pwzLogFile);
487 ReleaseStr(pwzDbFile);
488 ReleaseStr(pwzDatabaseEscaped);
489
490 return hr;
491}
492
493
494/********************************************************************
495 SqlDropDatabase - removes a database from a server if it exists
496
497 NOTE: wzInstance is optional
498 if fIntegratedAuth is set then wzUser and wzPassword are ignored
499********************************************************************/
500extern "C" HRESULT DAPI SqlDropDatabase(
501 __in_z LPCWSTR wzServer,
502 __in_z LPCWSTR wzInstance,
503 __in_z LPCWSTR wzDatabase,
504 __in BOOL fIntegratedAuth,
505 __in_z LPCWSTR wzUser,
506 __in_z LPCWSTR wzPassword,
507 __out_opt BSTR* pbstrErrorDescription
508 )
509{
510 Assert(wzServer && wzDatabase && *wzDatabase);
511
512 HRESULT hr = S_OK;
513 IDBCreateSession* pidbSession = NULL;
514
515 //
516 // connect to the master database to search for wzDatabase
517 //
518 hr = SqlConnectDatabase(wzServer, wzInstance, L"master", fIntegratedAuth, wzUser, wzPassword, &pidbSession);
519 SqlExitOnFailure(hr, "Failed to connect to 'master' database");
520
521 hr = SqlSessionDropDatabase(pidbSession, wzDatabase, pbstrErrorDescription);
522
523LExit:
524 ReleaseObject(pidbSession);
525
526 return hr;
527}
528
529
530/********************************************************************
531 SqlSessionDropDatabase - removes a database from a server if it exists
532
533 NOTE: pidbSession must be connected to the master database
534********************************************************************/
535extern "C" HRESULT DAPI SqlSessionDropDatabase(
536 __in IDBCreateSession* pidbSession,
537 __in_z LPCWSTR wzDatabase,
538 __out_opt BSTR* pbstrErrorDescription
539 )
540{
541 Assert(pidbSession && wzDatabase && *wzDatabase);
542
543 HRESULT hr = S_OK;
544 LPWSTR pwzQuery = NULL;
545 LPWSTR pwzDatabaseEscaped = NULL;
546
547 hr = SqlSessionDatabaseExists(pidbSession, wzDatabase, pbstrErrorDescription);
548 SqlExitOnFailure(hr, "failed to determine if exists, database: %ls", wzDatabase);
549
550 hr = EscapeSqlIdentifier(wzDatabase, &pwzDatabaseEscaped);
551 SqlExitOnFailure(hr, "failed to escape database string");
552
553 if (S_OK == hr)
554 {
555 hr = StrAllocFormatted(&pwzQuery, L"DROP DATABASE %s", pwzDatabaseEscaped);
556 SqlExitOnFailure(hr, "failed to allocate query to drop database: %ls", pwzDatabaseEscaped);
557
558 hr = SqlSessionExecuteQuery(pidbSession, pwzQuery, NULL, NULL, pbstrErrorDescription);
559 SqlExitOnFailure(hr, "Failed to drop database");
560 }
561
562LExit:
563 ReleaseStr(pwzQuery);
564 ReleaseStr(pwzDatabaseEscaped);
565
566 return hr;
567}
568
569
570/********************************************************************
571 SqlSessionExecuteQuery - executes a query and returns the results if desired
572
573 NOTE: ppirs and pcRoes and pbstrErrorDescription are optional
574********************************************************************/
575extern "C" HRESULT DAPI SqlSessionExecuteQuery(
576 __in IDBCreateSession* pidbSession,
577 __in __sql_command LPCWSTR wzSql,
578 __out_opt IRowset** ppirs,
579 __out_opt DBROWCOUNT* pcRows,
580 __out_opt BSTR* pbstrErrorDescription
581 )
582{
583 Assert(pidbSession);
584
585 HRESULT hr = S_OK;
586 IDBCreateCommand* pidbCommand = NULL;
587 ICommandText* picmdText = NULL;
588 ICommand* picmd = NULL;
589 DBROWCOUNT cRows = 0;
590
591 if (pcRows)
592 {
593 *pcRows = NULL;
594 }
595
596 //
597 // create the command
598 //
599 hr = pidbSession->CreateSession(NULL, IID_IDBCreateCommand, (IUnknown**)&pidbCommand);
600 SqlExitOnFailure(hr, "failed to create database session");
601 hr = pidbCommand->CreateCommand(NULL, IID_ICommand, (IUnknown**)&picmd);
602 SqlExitOnFailure(hr, "failed to create command to execute session");
603
604 //
605 // set the sql text into the command
606 //
607 hr = picmd->QueryInterface(IID_ICommandText, (LPVOID*)&picmdText);
608 SqlExitOnFailure(hr, "failed to get command text object for command");
609 hr = picmdText->SetCommandText(DBGUID_DEFAULT , wzSql);
610 SqlExitOnFailure(hr, "failed to set SQL string: %ls", wzSql);
611
612 //
613 // execute the command
614 //
615 hr = picmd->Execute(NULL, (ppirs) ? IID_IRowset : IID_NULL, NULL, &cRows, reinterpret_cast<IUnknown**>(ppirs));
616 SqlExitOnFailure(hr, "failed to execute SQL string: %ls", wzSql);
617
618 if (DB_S_ERRORSOCCURRED == hr)
619 {
620 hr = E_FAIL;
621 }
622
623 if (pcRows)
624 {
625 *pcRows = cRows;
626 }
627
628LExit:
629
630 if (FAILED(hr) && picmd && pbstrErrorDescription)
631 {
632 HRESULT hrGetErrors = SqlGetErrorInfo(picmd, IID_ICommandText, 0x409, NULL, pbstrErrorDescription); // TODO: use current locale instead of always American-English
633 if (FAILED(hrGetErrors))
634 {
635 ReleaseBSTR(*pbstrErrorDescription);
636 }
637 }
638
639 ReleaseObject(picmd);
640 ReleaseObject(picmdText);
641 ReleaseObject(pidbCommand);
642
643 return hr;
644}
645
646
647/********************************************************************
648 SqlCommandExecuteQuery - executes a SQL command and returns the results if desired
649
650 NOTE: ppirs and pcRoes are optional
651********************************************************************/
652extern "C" HRESULT DAPI SqlCommandExecuteQuery(
653 __in IDBCreateCommand* pidbCommand,
654 __in __sql_command LPCWSTR wzSql,
655 __out IRowset** ppirs,
656 __out DBROWCOUNT* pcRows
657 )
658{
659 Assert(pidbCommand);
660
661 HRESULT hr = S_OK;
662 ICommandText* picmdText = NULL;
663 ICommand* picmd = NULL;
664 DBROWCOUNT cRows = 0;
665
666 if (pcRows)
667 {
668 *pcRows = NULL;
669 }
670
671 //
672 // create the command
673 //
674 hr = pidbCommand->CreateCommand(NULL, IID_ICommand, (IUnknown**)&picmd);
675 SqlExitOnFailure(hr, "failed to create command to execute session");
676
677 //
678 // set the sql text into the command
679 //
680 hr = picmd->QueryInterface(IID_ICommandText, (LPVOID*)&picmdText);
681 SqlExitOnFailure(hr, "failed to get command text object for command");
682 hr = picmdText->SetCommandText(DBGUID_DEFAULT , wzSql);
683 SqlExitOnFailure(hr, "failed to set SQL string: %ls", wzSql);
684
685 //
686 // execute the command
687 //
688 hr = picmd->Execute(NULL, (ppirs) ? IID_IRowset : IID_NULL, NULL, &cRows, reinterpret_cast<IUnknown**>(ppirs));
689 SqlExitOnFailure(hr, "failed to execute SQL string: %ls", wzSql);
690
691 if (DB_S_ERRORSOCCURRED == hr)
692 {
693 hr = E_FAIL;
694 }
695
696 if (pcRows)
697 {
698 *pcRows = cRows;
699 }
700
701LExit:
702 ReleaseObject(picmd);
703 ReleaseObject(picmdText);
704
705 return hr;
706}
707
708
709/********************************************************************
710 SqlGetErrorInfo - gets error information from the last SQL function call
711
712 NOTE: pbstrErrorSource and pbstrErrorDescription are optional
713********************************************************************/
714extern "C" HRESULT DAPI SqlGetErrorInfo(
715 __in IUnknown* pObjectWithError,
716 __in REFIID IID_InterfaceWithError,
717 __in DWORD dwLocaleId,
718 __out_opt BSTR* pbstrErrorSource,
719 __out_opt BSTR* pbstrErrorDescription
720 )
721{
722 HRESULT hr = S_OK;
723 Assert(pObjectWithError);
724
725 // interfaces needed to extract error information out
726 ISupportErrorInfo* pISupportErrorInfo = NULL;
727 IErrorInfo* pIErrorInfoAll = NULL;
728 IErrorRecords* pIErrorRecords = NULL;
729 IErrorInfo* pIErrorInfoRecord = NULL;
730
731 // only ask for error information if the interface supports it.
732 hr = pObjectWithError->QueryInterface(IID_ISupportErrorInfo,(void**)&pISupportErrorInfo);
733 SqlExitOnFailure(hr, "No error information was found for object.");
734
735 hr = pISupportErrorInfo->InterfaceSupportsErrorInfo(IID_InterfaceWithError);
736 SqlExitOnFailure(hr, "InterfaceWithError is not supported for object with error");
737
738 // ignore the return of GetErrorInfo it can succeed and return a NULL pointer in pIErrorInfoAll anyway
739 hr = ::GetErrorInfo(0, &pIErrorInfoAll);
740 SqlExitOnFailure(hr, "failed to get error info");
741
742 if (S_OK == hr && pIErrorInfoAll)
743 {
744 // see if it's a valid OLE DB IErrorInfo interface that exposes a list of records
745 hr = pIErrorInfoAll->QueryInterface(IID_IErrorRecords, (void**)&pIErrorRecords);
746 if (SUCCEEDED(hr))
747 {
748 ULONG cErrors = 0;
749 pIErrorRecords->GetRecordCount(&cErrors);
750
751 // get the error information for each record
752 for (ULONG i = 0; i < cErrors; ++i)
753 {
754 hr = pIErrorRecords->GetErrorInfo(i, dwLocaleId, &pIErrorInfoRecord);
755 if (SUCCEEDED(hr))
756 {
757 if (pbstrErrorSource)
758 {
759 pIErrorInfoRecord->GetSource(pbstrErrorSource);
760 }
761 if (pbstrErrorDescription)
762 {
763 pIErrorInfoRecord->GetDescription(pbstrErrorDescription);
764 }
765
766 ReleaseNullObject(pIErrorInfoRecord);
767
768 break; // TODO: return more than one error in the future!
769 }
770 }
771
772 ReleaseNullObject(pIErrorRecords);
773 }
774 else // we have a simple error record
775 {
776 if (pbstrErrorSource)
777 {
778 pIErrorInfoAll->GetSource(pbstrErrorSource);
779 }
780 if (pbstrErrorDescription)
781 {
782 pIErrorInfoAll->GetDescription(pbstrErrorDescription);
783 }
784 }
785 }
786 else
787 {
788 hr = E_NOMOREITEMS;
789 }
790
791LExit:
792 ReleaseObject(pIErrorInfoRecord);
793 ReleaseObject(pIErrorRecords);
794 ReleaseObject(pIErrorInfoAll);
795 ReleaseObject(pISupportErrorInfo);
796
797 return hr;
798}
799
800
801//
802// private
803//
804
805static HRESULT InitializeDatabaseConnection(
806 __in REFCLSID rclsid,
807 __in_z LPCSTR szFriendlyClsidName,
808 __in DBPROPSET rgdbpsetInit[],
809 __in_ecount(rgdbpsetInit) DWORD cdbpsetInit,
810 __out IDBCreateSession** ppidbSession
811)
812{
813 Unused(szFriendlyClsidName); // only used in DEBUG builds
814
815 HRESULT hr = S_OK;
816 IDBInitialize* pidbInitialize = NULL;
817 IDBProperties* pidbProperties = NULL;
818
819 hr = ::CoCreateInstance(rclsid, NULL, CLSCTX_INPROC_SERVER, IID_IDBInitialize, (LPVOID*)&pidbInitialize);
820 SqlExitOnFailure(hr, "failed to initialize %s", szFriendlyClsidName);
821
822 // create and set the property set
823 hr = pidbInitialize->QueryInterface(IID_IDBProperties, (LPVOID*)&pidbProperties);
824 SqlExitOnFailure(hr, "failed to get IID_IDBProperties for %s", szFriendlyClsidName);
825
826 hr = pidbProperties->SetProperties(cdbpsetInit, rgdbpsetInit);
827 SqlExitOnFailure(hr, "failed to set properties for %s", szFriendlyClsidName);
828
829 // initialize connection to datasource
830 hr = pidbInitialize->Initialize();
831 if (FAILED(hr))
832 {
833 DumpErrorRecords();
834 }
835 SqlExitOnFailure(hr, "failed to initialize connection for %s", szFriendlyClsidName);
836
837 hr = pidbInitialize->QueryInterface(IID_IDBCreateSession, (LPVOID*)ppidbSession);
838 SqlExitOnFailure(hr, "failed to query for connection session for %s", szFriendlyClsidName);
839
840LExit:
841 ReleaseObject(pidbProperties);
842 ReleaseObject(pidbInitialize);
843
844 return hr;
845}
846
847HRESULT DumpErrorRecords()
848{
849 HRESULT hr = S_OK;
850 IErrorInfo* pIErrorInfo = NULL;
851 IErrorRecords* pIErrorRecords = NULL;
852 IErrorInfo* pIErrorInfoRecord = NULL;
853 BSTR bstrDescription = NULL;
854 ULONG i = 0;
855 ULONG cRecords = 0;
856 ERRORINFO ErrorInfo = { };
857
858 // Get IErrorInfo pointer from OLE.
859 hr = ::GetErrorInfo(0, &pIErrorInfo);
860 if (FAILED(hr))
861 {
862 ExitFunction();
863 }
864
865 // QI for IID_IErrorRecords.
866 hr = pIErrorInfo->QueryInterface(IID_IErrorRecords, (void**)&pIErrorRecords);
867 if (FAILED(hr))
868 {
869 ExitFunction();
870 }
871
872 // Get error record count.
873 hr = pIErrorRecords->GetRecordCount(&cRecords);
874 if (FAILED(hr))
875 {
876 ExitFunction();
877 }
878
879 // Loop through the error records.
880 for (i = 0; i < cRecords; i++)
881 {
882 // Get pIErrorInfo from pIErrorRecords.
883 hr = pIErrorRecords->GetErrorInfo(i, 1033, &pIErrorInfoRecord);
884
885 if (SUCCEEDED(hr))
886 {
887 // Get error description and source.
888 hr = pIErrorInfoRecord->GetDescription(&bstrDescription);
889
890 // Retrieve the ErrorInfo structures.
891 hr = pIErrorRecords->GetBasicErrorInfo(i, &ErrorInfo);
892
893 SqlExitTrace(ErrorInfo.hrError, "SQL error %lu/%lu: %ls", i + 1, cRecords, bstrDescription);
894
895 ReleaseNullObject(pIErrorInfoRecord);
896 ReleaseNullBSTR(bstrDescription);
897 }
898 }
899
900LExit:
901 ReleaseNullBSTR(bstrDescription);
902 ReleaseObject(pIErrorInfoRecord);
903 ReleaseObject(pIErrorRecords);
904 ReleaseObject(pIErrorInfo);
905
906 return hr;
907}
908
909/********************************************************************
910 FileSpecToString
911
912*********************************************************************/
913static HRESULT FileSpecToString(
914 __in const SQL_FILESPEC* psf,
915 __out LPWSTR* ppwz
916 )
917{
918 Assert(psf && ppwz);
919
920 HRESULT hr = S_OK;
921 LPWSTR pwz = NULL;
922
923 hr = StrAllocString(&pwz, L"(", 1024);
924 SqlExitOnFailure(hr, "failed to allocate string for database file info");
925
926 SqlExitOnNull(*psf->wzName, hr, E_INVALIDARG, "logical name not specified in database file info");
927 SqlExitOnNull(*psf->wzFilename, hr, E_INVALIDARG, "filename not specified in database file info");
928
929 hr = StrAllocFormatted(&pwz, L"%sNAME=%s", pwz, psf->wzName);
930 SqlExitOnFailure(hr, "failed to format database file info name: %ls", psf->wzName);
931
932 hr = StrAllocFormatted(&pwz, L"%s, FILENAME='%s'", pwz, psf->wzFilename);
933 SqlExitOnFailure(hr, "failed to format database file info filename: %ls", psf->wzFilename);
934
935 if (0 != psf->wzSize[0])
936 {
937 hr = StrAllocFormatted(&pwz, L"%s, SIZE=%s", pwz, psf->wzSize);
938 SqlExitOnFailure(hr, "failed to format database file info size: %ls", psf->wzSize);
939 }
940
941 if (0 != psf->wzMaxSize[0])
942 {
943 hr = StrAllocFormatted(&pwz, L"%s, MAXSIZE=%s", pwz, psf->wzMaxSize);
944 SqlExitOnFailure(hr, "failed to format database file info maxsize: %ls", psf->wzMaxSize);
945 }
946
947 if (0 != psf->wzGrow[0])
948 {
949 hr = StrAllocFormatted(&pwz, L"%s, FILEGROWTH=%s", pwz, psf->wzGrow);
950 SqlExitOnFailure(hr, "failed to format database file info growth: %ls", psf->wzGrow);
951 }
952
953 hr = StrAllocFormatted(&pwz, L"%s)", pwz);
954 SqlExitOnFailure(hr, "failed to allocate string for file spec");
955
956 *ppwz = pwz;
957 pwz = NULL; // null here so it doesn't get freed below
958
959LExit:
960 ReleaseStr(pwz);
961 return hr;
962}
963
964static HRESULT EscapeSqlIdentifier(
965 __in_z LPCWSTR wzIdentifier,
966 __deref_out_z LPWSTR* ppwz
967 )
968{
969 Assert(ppwz);
970
971 HRESULT hr = S_OK;
972 LPWSTR pwz = NULL;
973
974 if (wzIdentifier == NULL)
975 {
976 //Just ignore a NULL identifier and clear out the result
977 ReleaseNullStr(*ppwz);
978 ExitFunction();
979 }
980
981 int cchIdentifier = lstrlenW(wzIdentifier);
982
983 //If an empty string or already escaped just copy
984 if (cchIdentifier == 0 || (wzIdentifier[0] == '[' && wzIdentifier[cchIdentifier-1] == ']'))
985 {
986 hr = StrAllocString(&pwz, wzIdentifier, 0);
987 SqlExitOnFailure(hr, "failed to format database name: %ls", wzIdentifier);
988 }
989 else
990 {
991 //escape it
992 hr = StrAllocFormatted(&pwz, L"[%s]", wzIdentifier);
993 SqlExitOnFailure(hr, "failed to format escaped database name: %ls", wzIdentifier);
994 }
995
996 *ppwz = pwz;
997 pwz = NULL; // null here so it doesn't get freed below
998
999LExit:
1000 ReleaseStr(pwz);
1001 return hr;
1002}
diff --git a/src/libs/dutil/WixToolset.DUtil/srputil.cpp b/src/libs/dutil/WixToolset.DUtil/srputil.cpp
new file mode 100644
index 00000000..e44536cc
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/srputil.cpp
@@ -0,0 +1,252 @@
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// Exit macros
7#define SrpExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_SRPUTIL, x, s, __VA_ARGS__)
8#define SrpExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_SRPUTIL, x, s, __VA_ARGS__)
9#define SrpExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_SRPUTIL, x, s, __VA_ARGS__)
10#define SrpExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_SRPUTIL, x, s, __VA_ARGS__)
11#define SrpExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_SRPUTIL, x, s, __VA_ARGS__)
12#define SrpExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_SRPUTIL, x, s, __VA_ARGS__)
13#define SrpExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_SRPUTIL, p, x, e, s, __VA_ARGS__)
14#define SrpExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_SRPUTIL, p, x, s, __VA_ARGS__)
15#define SrpExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_SRPUTIL, p, x, e, s, __VA_ARGS__)
16#define SrpExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_SRPUTIL, p, x, s, __VA_ARGS__)
17#define SrpExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_SRPUTIL, e, x, s, __VA_ARGS__)
18#define SrpExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_SRPUTIL, g, x, s, __VA_ARGS__)
19
20
21typedef BOOL (WINAPI *PFN_SETRESTOREPTW)(
22 __in PRESTOREPOINTINFOW pRestorePtSpec,
23 __out PSTATEMGRSTATUS pSMgrStatus
24 );
25
26static PFN_SETRESTOREPTW vpfnSRSetRestorePointW = NULL;
27static HMODULE vhSrClientDll = NULL;
28
29
30static HRESULT InitializeComSecurity();
31
32
33DAPI_(HRESULT) SrpInitialize(
34 __in BOOL fInitializeComSecurity
35 )
36{
37 HRESULT hr = S_OK;
38
39 hr = LoadSystemLibrary(L"srclient.dll", &vhSrClientDll);
40 if (FAILED(hr))
41 {
42 ExitFunction1(hr = E_NOTIMPL);
43 }
44
45 vpfnSRSetRestorePointW = reinterpret_cast<PFN_SETRESTOREPTW>(::GetProcAddress(vhSrClientDll, "SRSetRestorePointW"));
46 SrpExitOnNullWithLastError(vpfnSRSetRestorePointW, hr, "Failed to find set restore point proc address.");
47
48 // If allowed, initialize COM security to enable NetworkService,
49 // LocalService and System to make callbacks to the process
50 // calling System Restore. This is required for any process
51 // that calls SRSetRestorePoint.
52 if (fInitializeComSecurity)
53 {
54 hr = InitializeComSecurity();
55 SrpExitOnFailure(hr, "Failed to initialize security for COM to talk to system restore.");
56 }
57
58LExit:
59 if (FAILED(hr) && vhSrClientDll)
60 {
61 SrpUninitialize();
62 }
63
64 return hr;
65}
66
67DAPI_(void) SrpUninitialize()
68{
69 if (vhSrClientDll)
70 {
71 ::FreeLibrary(vhSrClientDll);
72 vhSrClientDll = NULL;
73 vpfnSRSetRestorePointW = NULL;
74 }
75}
76
77DAPI_(HRESULT) SrpCreateRestorePoint(
78 __in_z LPCWSTR wzApplicationName,
79 __in SRP_ACTION action
80 )
81{
82 HRESULT hr = S_OK;
83 RESTOREPOINTINFOW restorePoint = { };
84 STATEMGRSTATUS status = { };
85
86 if (!vpfnSRSetRestorePointW)
87 {
88 ExitFunction1(hr = E_NOTIMPL);
89 }
90
91 restorePoint.dwEventType = BEGIN_SYSTEM_CHANGE;
92 restorePoint.dwRestorePtType = (SRP_ACTION_INSTALL == action) ? APPLICATION_INSTALL : (SRP_ACTION_UNINSTALL == action) ? APPLICATION_UNINSTALL : MODIFY_SETTINGS;
93 ::StringCbCopyW(restorePoint.szDescription, sizeof(restorePoint.szDescription), wzApplicationName);
94
95 if (!vpfnSRSetRestorePointW(&restorePoint, &status))
96 {
97 SrpExitOnWin32Error(status.nStatus, hr, "Failed to create system restore point.");
98 }
99
100LExit:
101 return hr;
102}
103
104
105// internal functions.
106
107static HRESULT InitializeComSecurity()
108{
109 HRESULT hr = S_OK;
110 DWORD er = ERROR_SUCCESS;
111 SECURITY_DESCRIPTOR sd = {0};
112 EXPLICIT_ACCESS ea[5] = {0};
113 ACL* pAcl = NULL;
114 ULONGLONG rgSidBA[(SECURITY_MAX_SID_SIZE+sizeof(ULONGLONG)-1)/sizeof(ULONGLONG)]={0};
115 ULONGLONG rgSidLS[(SECURITY_MAX_SID_SIZE+sizeof(ULONGLONG)-1)/sizeof(ULONGLONG)]={0};
116 ULONGLONG rgSidNS[(SECURITY_MAX_SID_SIZE+sizeof(ULONGLONG)-1)/sizeof(ULONGLONG)]={0};
117 ULONGLONG rgSidPS[(SECURITY_MAX_SID_SIZE+sizeof(ULONGLONG)-1)/sizeof(ULONGLONG)]={0};
118 ULONGLONG rgSidSY[(SECURITY_MAX_SID_SIZE+sizeof(ULONGLONG)-1)/sizeof(ULONGLONG)]={0};
119 DWORD cbSid = 0;
120
121 // Create the security descriptor explicitly as follows because
122 // CoInitializeSecurity() will not accept the relative security descriptors
123 // returned by ConvertStringSecurityDescriptorToSecurityDescriptor().
124 //
125 // The result is a security descriptor that is equivalent to the following
126 // security descriptor definition language (SDDL) string:
127 //
128 // O:BAG:BAD:(A;;0x1;;;LS)(A;;0x1;;;NS)(A;;0x1;;;PS)(A;;0x1;;;SY)(A;;0x1;;;BA)
129 //
130
131 // Initialize the security descriptor.
132 if (!::InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION))
133 {
134 SrpExitWithLastError(hr, "Failed to initialize security descriptor for system restore.");
135 }
136
137 // Create an administrator group security identifier (SID).
138 cbSid = sizeof(rgSidBA);
139 if (!::CreateWellKnownSid(WinBuiltinAdministratorsSid, NULL, rgSidBA, &cbSid))
140 {
141 SrpExitWithLastError(hr, "Failed to create administrator SID for system restore.");
142 }
143
144 // Create a local service security identifier (SID).
145 cbSid = sizeof(rgSidLS);
146 if (!::CreateWellKnownSid(WinLocalServiceSid, NULL, rgSidLS, &cbSid))
147 {
148 SrpExitWithLastError(hr, "Failed to create local service SID for system restore.");
149 }
150
151 // Create a network service security identifier (SID).
152 cbSid = sizeof(rgSidNS);
153 if (!::CreateWellKnownSid(WinNetworkServiceSid, NULL, rgSidNS, &cbSid))
154 {
155 SrpExitWithLastError(hr, "Failed to create network service SID for system restore.");
156 }
157
158 // Create a personal account security identifier (SID).
159 cbSid = sizeof(rgSidPS);
160 if (!::CreateWellKnownSid(WinSelfSid, NULL, rgSidPS, &cbSid))
161 {
162 SrpExitWithLastError(hr, "Failed to create self SID for system restore.");
163 }
164
165 // Create a local service security identifier (SID).
166 cbSid = sizeof(rgSidSY);
167 if (!::CreateWellKnownSid(WinLocalSystemSid, NULL, rgSidSY, &cbSid))
168 {
169 SrpExitWithLastError(hr, "Failed to create local system SID for system restore.");
170 }
171
172 // Setup the access control entries (ACE) for COM. COM_RIGHTS_EXECUTE and
173 // COM_RIGHTS_EXECUTE_LOCAL are the minimum access rights required.
174 ea[0].grfAccessPermissions = COM_RIGHTS_EXECUTE | COM_RIGHTS_EXECUTE_LOCAL;
175 ea[0].grfAccessMode = SET_ACCESS;
176 ea[0].grfInheritance = NO_INHERITANCE;
177 ea[0].Trustee.pMultipleTrustee = NULL;
178 ea[0].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
179 ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
180 ea[0].Trustee.TrusteeType = TRUSTEE_IS_GROUP;
181 ea[0].Trustee.ptstrName = (LPTSTR)rgSidBA;
182
183 ea[1].grfAccessPermissions = COM_RIGHTS_EXECUTE | COM_RIGHTS_EXECUTE_LOCAL;
184 ea[1].grfAccessMode = SET_ACCESS;
185 ea[1].grfInheritance = NO_INHERITANCE;
186 ea[1].Trustee.pMultipleTrustee = NULL;
187 ea[1].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
188 ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
189 ea[1].Trustee.TrusteeType = TRUSTEE_IS_GROUP;
190 ea[1].Trustee.ptstrName = (LPTSTR)rgSidLS;
191
192 ea[2].grfAccessPermissions = COM_RIGHTS_EXECUTE | COM_RIGHTS_EXECUTE_LOCAL;
193 ea[2].grfAccessMode = SET_ACCESS;
194 ea[2].grfInheritance = NO_INHERITANCE;
195 ea[2].Trustee.pMultipleTrustee = NULL;
196 ea[2].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
197 ea[2].Trustee.TrusteeForm = TRUSTEE_IS_SID;
198 ea[2].Trustee.TrusteeType = TRUSTEE_IS_GROUP;
199 ea[2].Trustee.ptstrName = (LPTSTR)rgSidNS;
200
201 ea[3].grfAccessPermissions = COM_RIGHTS_EXECUTE | COM_RIGHTS_EXECUTE_LOCAL;
202 ea[3].grfAccessMode = SET_ACCESS;
203 ea[3].grfInheritance = NO_INHERITANCE;
204 ea[3].Trustee.pMultipleTrustee = NULL;
205 ea[3].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
206 ea[3].Trustee.TrusteeForm = TRUSTEE_IS_SID;
207 ea[3].Trustee.TrusteeType = TRUSTEE_IS_GROUP;
208 ea[3].Trustee.ptstrName = (LPTSTR)rgSidPS;
209
210 ea[4].grfAccessPermissions = COM_RIGHTS_EXECUTE | COM_RIGHTS_EXECUTE_LOCAL;
211 ea[4].grfAccessMode = SET_ACCESS;
212 ea[4].grfInheritance = NO_INHERITANCE;
213 ea[4].Trustee.pMultipleTrustee = NULL;
214 ea[4].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
215 ea[4].Trustee.TrusteeForm = TRUSTEE_IS_SID;
216 ea[4].Trustee.TrusteeType = TRUSTEE_IS_GROUP;
217 ea[4].Trustee.ptstrName = (LPTSTR)rgSidSY;
218
219 // Create an access control list (ACL) using this ACE list.
220 er = ::SetEntriesInAcl(countof(ea), ea, NULL, &pAcl);
221 SrpExitOnWin32Error(er, hr, "Failed to create ACL for system restore.");
222
223 // Set the security descriptor owner to Administrators.
224 if (!::SetSecurityDescriptorOwner(&sd, rgSidBA, FALSE))
225 {
226 SrpExitWithLastError(hr, "Failed to set administrators owner for system restore.");
227 }
228
229 // Set the security descriptor group to Administrators.
230 if (!::SetSecurityDescriptorGroup(&sd, rgSidBA, FALSE))
231 {
232 SrpExitWithLastError(hr, "Failed to set administrators group access for system restore.");
233 }
234
235 // Set the discretionary access control list (DACL) to the ACL.
236 if (!::SetSecurityDescriptorDacl(&sd, TRUE, pAcl, FALSE))
237 {
238 SrpExitWithLastError(hr, "Failed to set DACL for system restore.");
239 }
240
241 // Note that an explicit security descriptor is being passed in.
242 hr= ::CoInitializeSecurity(&sd, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_IMP_LEVEL_IDENTIFY, NULL, EOAC_DISABLE_AAA | EOAC_NO_CUSTOM_MARSHAL, NULL);
243 SrpExitOnFailure(hr, "Failed to initialize COM security for system restore.");
244
245LExit:
246 if (pAcl)
247 {
248 ::LocalFree(pAcl);
249 }
250
251 return hr;
252}
diff --git a/src/libs/dutil/WixToolset.DUtil/strutil.cpp b/src/libs/dutil/WixToolset.DUtil/strutil.cpp
new file mode 100644
index 00000000..550d6169
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/strutil.cpp
@@ -0,0 +1,2824 @@
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// Exit macros
7#define StrExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_STRUTIL, x, s, __VA_ARGS__)
8#define StrExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_STRUTIL, x, s, __VA_ARGS__)
9#define StrExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_STRUTIL, x, s, __VA_ARGS__)
10#define StrExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_STRUTIL, x, s, __VA_ARGS__)
11#define StrExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_STRUTIL, x, s, __VA_ARGS__)
12#define StrExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_STRUTIL, x, s, __VA_ARGS__)
13#define StrExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_STRUTIL, p, x, e, s, __VA_ARGS__)
14#define StrExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_STRUTIL, p, x, s, __VA_ARGS__)
15#define StrExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_STRUTIL, p, x, e, s, __VA_ARGS__)
16#define StrExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_STRUTIL, p, x, s, __VA_ARGS__)
17#define StrExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_STRUTIL, e, x, s, __VA_ARGS__)
18#define StrExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_STRUTIL, g, x, s, __VA_ARGS__)
19
20#define ARRAY_GROWTH_SIZE 5
21
22// Forward declarations.
23static HRESULT AllocHelper(
24 __deref_out_ecount_part(cch, 0) LPWSTR* ppwz,
25 __in SIZE_T cch,
26 __in BOOL fZeroOnRealloc
27 );
28static HRESULT AllocStringHelper(
29 __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz,
30 __in_z LPCWSTR wzSource,
31 __in SIZE_T cchSource,
32 __in BOOL fZeroOnRealloc
33 );
34static HRESULT AllocConcatHelper(
35 __deref_out_z LPWSTR* ppwz,
36 __in_z LPCWSTR wzSource,
37 __in SIZE_T cchSource,
38 __in BOOL fZeroOnRealloc
39 );
40static HRESULT AllocFormattedArgsHelper(
41 __deref_out_z LPWSTR* ppwz,
42 __in BOOL fZeroOnRealloc,
43 __in __format_string LPCWSTR wzFormat,
44 __in va_list args
45 );
46static HRESULT StrAllocStringMapInvariant(
47 __deref_out_z LPWSTR* pscz,
48 __in_z LPCWSTR wzSource,
49 __in SIZE_T cchSource,
50 __in DWORD dwMapFlags
51 );
52
53/********************************************************************
54StrAlloc - allocates or reuses dynamic string memory
55
56NOTE: caller is responsible for freeing ppwz even if function fails
57********************************************************************/
58extern "C" HRESULT DAPI StrAlloc(
59 __deref_out_ecount_part(cch, 0) LPWSTR* ppwz,
60 __in SIZE_T cch
61 )
62{
63 return AllocHelper(ppwz, cch, FALSE);
64}
65
66/********************************************************************
67StrAllocSecure - allocates or reuses dynamic string memory
68If the memory needs to reallocated, calls SecureZeroMemory on the
69original block of memory after it is moved.
70
71NOTE: caller is responsible for freeing ppwz even if function fails
72********************************************************************/
73extern "C" HRESULT DAPI StrAllocSecure(
74 __deref_out_ecount_part(cch, 0) LPWSTR* ppwz,
75 __in SIZE_T cch
76 )
77{
78 return AllocHelper(ppwz, cch, TRUE);
79}
80
81/********************************************************************
82AllocHelper - allocates or reuses dynamic string memory
83If fZeroOnRealloc is true and the memory needs to reallocated,
84calls SecureZeroMemory on original block of memory after it is moved.
85
86NOTE: caller is responsible for freeing ppwz even if function fails
87********************************************************************/
88static HRESULT AllocHelper(
89 __deref_out_ecount_part(cch, 0) LPWSTR* ppwz,
90 __in SIZE_T cch,
91 __in BOOL fZeroOnRealloc
92 )
93{
94 Assert(ppwz && cch);
95
96 HRESULT hr = S_OK;
97 LPWSTR pwz = NULL;
98
99 if (cch >= MAXDWORD / sizeof(WCHAR))
100 {
101 hr = E_OUTOFMEMORY;
102 StrExitOnFailure(hr, "Not enough memory to allocate string of size: %u", cch);
103 }
104
105 if (*ppwz)
106 {
107 if (fZeroOnRealloc)
108 {
109 LPVOID pvNew = NULL;
110 hr = MemReAllocSecure(*ppwz, sizeof(WCHAR)* cch, FALSE, &pvNew);
111 StrExitOnFailure(hr, "Failed to reallocate string");
112 pwz = static_cast<LPWSTR>(pvNew);
113 }
114 else
115 {
116 pwz = static_cast<LPWSTR>(MemReAlloc(*ppwz, sizeof(WCHAR)* cch, FALSE));
117 }
118 }
119 else
120 {
121 pwz = static_cast<LPWSTR>(MemAlloc(sizeof(WCHAR) * cch, TRUE));
122 }
123
124 StrExitOnNull(pwz, hr, E_OUTOFMEMORY, "failed to allocate string, len: %u", cch);
125
126 *ppwz = pwz;
127LExit:
128 return hr;
129}
130
131
132/********************************************************************
133StrTrimCapacity - Frees any unnecessary memory associated with a string.
134 Purely used for optimization, generally only when a string
135 has been changing size, and will no longer grow.
136
137NOTE: caller is responsible for freeing ppwz even if function fails
138********************************************************************/
139HRESULT DAPI StrTrimCapacity(
140 __deref_out_z LPWSTR* ppwz
141 )
142{
143 Assert(ppwz);
144
145 HRESULT hr = S_OK;
146 SIZE_T cchLen = 0;
147
148 hr = ::StringCchLengthW(*ppwz, STRSAFE_MAX_CCH, reinterpret_cast<UINT_PTR*>(&cchLen));
149 StrExitOnRootFailure(hr, "Failed to calculate length of string");
150
151 ++cchLen; // Add 1 for null-terminator
152
153 hr = StrAlloc(ppwz, cchLen);
154 StrExitOnFailure(hr, "Failed to reallocate string");
155
156LExit:
157 return hr;
158}
159
160
161/********************************************************************
162StrTrimWhitespace - allocates or reuses dynamic string memory and copies
163 in an existing string, excluding whitespace
164
165NOTE: caller is responsible for freeing ppwz even if function fails
166********************************************************************/
167HRESULT DAPI StrTrimWhitespace(
168 __deref_out_z LPWSTR* ppwz,
169 __in_z LPCWSTR wzSource
170 )
171{
172 HRESULT hr = S_OK;
173 size_t i = 0;
174 LPWSTR sczResult = NULL;
175
176 // Ignore beginning whitespace
177 while (L' ' == *wzSource || L'\t' == *wzSource)
178 {
179 wzSource++;
180 }
181
182 hr = ::StringCchLengthW(wzSource, STRSAFE_MAX_CCH, &i);
183 StrExitOnRootFailure(hr, "Failed to get length of string");
184
185 // Overwrite ending whitespace with null characters
186 if (0 < i)
187 {
188 // start from the last non-null-terminator character in the array
189 for (i = i - 1; i > 0; --i)
190 {
191 if (L' ' != wzSource[i] && L'\t' != wzSource[i])
192 {
193 break;
194 }
195 }
196
197 ++i;
198 }
199
200 hr = StrAllocString(&sczResult, wzSource, i);
201 StrExitOnFailure(hr, "Failed to copy result string");
202
203 // Output result
204 *ppwz = sczResult;
205 sczResult = NULL;
206
207LExit:
208 ReleaseStr(sczResult);
209
210 return hr;
211}
212
213
214/********************************************************************
215StrAnsiAlloc - allocates or reuses dynamic ANSI string memory
216
217NOTE: caller is responsible for freeing ppsz even if function fails
218********************************************************************/
219extern "C" HRESULT DAPI StrAnsiAlloc(
220 __deref_out_ecount_part(cch, 0) LPSTR* ppsz,
221 __in SIZE_T cch
222 )
223{
224 Assert(ppsz && cch);
225
226 HRESULT hr = S_OK;
227 LPSTR psz = NULL;
228
229 if (cch >= MAXDWORD / sizeof(WCHAR))
230 {
231 hr = E_OUTOFMEMORY;
232 StrExitOnFailure(hr, "Not enough memory to allocate string of size: %u", cch);
233 }
234
235 if (*ppsz)
236 {
237 psz = static_cast<LPSTR>(MemReAlloc(*ppsz, sizeof(CHAR) * cch, FALSE));
238 }
239 else
240 {
241 psz = static_cast<LPSTR>(MemAlloc(sizeof(CHAR) * cch, TRUE));
242 }
243
244 StrExitOnNull(psz, hr, E_OUTOFMEMORY, "failed to allocate string, len: %u", cch);
245
246 *ppsz = psz;
247LExit:
248 return hr;
249}
250
251
252/********************************************************************
253StrAnsiTrimCapacity - Frees any unnecessary memory associated with a string.
254 Purely used for optimization, generally only when a string
255 has been changing size, and will no longer grow.
256
257NOTE: caller is responsible for freeing ppwz even if function fails
258********************************************************************/
259HRESULT DAPI StrAnsiTrimCapacity(
260 __deref_out_z LPSTR* ppz
261 )
262{
263 Assert(ppz);
264
265 HRESULT hr = S_OK;
266 SIZE_T cchLen = 0;
267
268#pragma prefast(push)
269#pragma prefast(disable:25068)
270 hr = ::StringCchLengthA(*ppz, STRSAFE_MAX_CCH, reinterpret_cast<UINT_PTR*>(&cchLen));
271#pragma prefast(pop)
272 StrExitOnFailure(hr, "Failed to calculate length of string");
273
274 ++cchLen; // Add 1 for null-terminator
275
276 hr = StrAnsiAlloc(ppz, cchLen);
277 StrExitOnFailure(hr, "Failed to reallocate string");
278
279LExit:
280 return hr;
281}
282
283
284/********************************************************************
285StrAnsiTrimWhitespace - allocates or reuses dynamic string memory and copies
286 in an existing string, excluding whitespace
287
288NOTE: caller is responsible for freeing ppz even if function fails
289********************************************************************/
290HRESULT DAPI StrAnsiTrimWhitespace(
291 __deref_out_z LPSTR* ppz,
292 __in_z LPCSTR szSource
293 )
294{
295 HRESULT hr = S_OK;
296 size_t i = 0;
297 LPSTR sczResult = NULL;
298
299 // Ignore beginning whitespace
300 while (' ' == *szSource || '\t' == *szSource)
301 {
302 szSource++;
303 }
304
305 hr = ::StringCchLengthA(szSource, STRSAFE_MAX_CCH, &i);
306 StrExitOnRootFailure(hr, "Failed to get length of string");
307
308 // Overwrite ending whitespace with null characters
309 if (0 < i)
310 {
311 // start from the last non-null-terminator character in the array
312 for (i = i - 1; i > 0; --i)
313 {
314 if (L' ' != szSource[i] && L'\t' != szSource[i])
315 {
316 break;
317 }
318 }
319
320 ++i;
321 }
322
323 hr = StrAnsiAllocStringAnsi(&sczResult, szSource, i);
324 StrExitOnFailure(hr, "Failed to copy result string");
325
326 // Output result
327 *ppz = sczResult;
328 sczResult = NULL;
329
330LExit:
331 ReleaseStr(sczResult);
332
333 return hr;
334}
335
336/********************************************************************
337StrAllocString - allocates or reuses dynamic string memory and copies in an existing string
338
339NOTE: caller is responsible for freeing ppwz even if function fails
340NOTE: cchSource does not have to equal the length of wzSource
341NOTE: if cchSource == 0, length of wzSource is used instead
342********************************************************************/
343extern "C" HRESULT DAPI StrAllocString(
344 __deref_out_ecount_z(cchSource+1) LPWSTR* ppwz,
345 __in_z LPCWSTR wzSource,
346 __in SIZE_T cchSource
347 )
348{
349 return AllocStringHelper(ppwz, wzSource, cchSource, FALSE);
350}
351
352/********************************************************************
353StrAllocStringSecure - allocates or reuses dynamic string memory and
354copies in an existing string. If the memory needs to reallocated,
355calls SecureZeroMemory on original block of memory after it is moved.
356
357NOTE: caller is responsible for freeing ppwz even if function fails
358NOTE: cchSource does not have to equal the length of wzSource
359NOTE: if cchSource == 0, length of wzSource is used instead
360********************************************************************/
361extern "C" HRESULT DAPI StrAllocStringSecure(
362 __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz,
363 __in_z LPCWSTR wzSource,
364 __in SIZE_T cchSource
365 )
366{
367 return AllocStringHelper(ppwz, wzSource, cchSource, TRUE);
368}
369
370/********************************************************************
371AllocStringHelper - allocates or reuses dynamic string memory and copies in an existing string
372If fZeroOnRealloc is true and the memory needs to reallocated,
373calls SecureZeroMemory on original block of memory after it is moved.
374
375NOTE: caller is responsible for freeing ppwz even if function fails
376NOTE: cchSource does not have to equal the length of wzSource
377NOTE: if cchSource == 0, length of wzSource is used instead
378********************************************************************/
379static HRESULT AllocStringHelper(
380 __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz,
381 __in_z LPCWSTR wzSource,
382 __in SIZE_T cchSource,
383 __in BOOL fZeroOnRealloc
384 )
385{
386 Assert(ppwz && wzSource); // && *wzSource);
387
388 HRESULT hr = S_OK;
389 SIZE_T cch = 0;
390
391 if (*ppwz)
392 {
393 cch = MemSize(*ppwz); // get the count in bytes so we can check if it failed (returns -1)
394 if (-1 == cch)
395 {
396 hr = E_INVALIDARG;
397 StrExitOnFailure(hr, "failed to get size of destination string");
398 }
399 cch /= sizeof(WCHAR); //convert the count in bytes to count in characters
400 }
401
402 if (0 == cchSource && wzSource)
403 {
404 hr = ::StringCchLengthW(wzSource, STRSAFE_MAX_CCH, reinterpret_cast<size_t*>(&cchSource));
405 StrExitOnRootFailure(hr, "failed to get length of source string");
406 }
407
408 SIZE_T cchNeeded;
409 hr = ::ULongPtrAdd(cchSource, 1, &cchNeeded); // add one for the null terminator
410 StrExitOnRootFailure(hr, "source string is too long");
411
412 if (cch < cchNeeded)
413 {
414 cch = cchNeeded;
415 hr = AllocHelper(ppwz, cch, fZeroOnRealloc);
416 StrExitOnFailure(hr, "failed to allocate string from string.");
417 }
418
419 // copy everything (the NULL terminator will be included)
420 hr = ::StringCchCopyNExW(*ppwz, cch, wzSource, cchSource, NULL, NULL, STRSAFE_FILL_BEHIND_NULL);
421
422LExit:
423 return hr;
424}
425
426
427/********************************************************************
428StrAnsiAllocString - allocates or reuses dynamic ANSI string memory and copies in an existing string
429
430NOTE: caller is responsible for freeing ppsz even if function fails
431NOTE: cchSource must equal the length of wzSource (not including the NULL terminator)
432NOTE: if cchSource == 0, length of wzSource is used instead
433********************************************************************/
434extern "C" HRESULT DAPI StrAnsiAllocString(
435 __deref_out_ecount_z(cchSource+1) LPSTR* ppsz,
436 __in_z LPCWSTR wzSource,
437 __in SIZE_T cchSource,
438 __in UINT uiCodepage
439 )
440{
441 Assert(ppsz && wzSource);
442
443 HRESULT hr = S_OK;
444 LPSTR psz = NULL;
445 SIZE_T cch = 0;
446 SIZE_T cchDest = cchSource; // at least enough
447
448 if (*ppsz)
449 {
450 cch = MemSize(*ppsz); // get the count in bytes so we can check if it failed (returns -1)
451 if (-1 == cch)
452 {
453 hr = E_INVALIDARG;
454 StrExitOnFailure(hr, "failed to get size of destination string");
455 }
456 cch /= sizeof(CHAR); //convert the count in bytes to count in characters
457 }
458
459 if (0 == cchSource)
460 {
461 cchDest = ::WideCharToMultiByte(uiCodepage, 0, wzSource, -1, NULL, 0, NULL, NULL);
462 if (0 == cchDest)
463 {
464 StrExitWithLastError(hr, "failed to get required size for conversion to ANSI: %ls", wzSource);
465 }
466
467 --cchDest; // subtract one because WideChageToMultiByte includes space for the NULL terminator that we track below
468 }
469 else if (L'\0' == wzSource[cchSource - 1]) // if the source already had a null terminator, don't count that in the character count because we track it below
470 {
471 cchDest = cchSource - 1;
472 }
473
474 if (cch < cchDest + 1)
475 {
476 cch = cchDest + 1; // add one for the NULL terminator
477 if (cch >= MAXDWORD / sizeof(WCHAR))
478 {
479 hr = E_OUTOFMEMORY;
480 StrExitOnFailure(hr, "Not enough memory to allocate string of size: %u", cch);
481 }
482
483 if (*ppsz)
484 {
485 psz = static_cast<LPSTR>(MemReAlloc(*ppsz, sizeof(CHAR) * cch, TRUE));
486 }
487 else
488 {
489 psz = static_cast<LPSTR>(MemAlloc(sizeof(CHAR) * cch, TRUE));
490 }
491 StrExitOnNull(psz, hr, E_OUTOFMEMORY, "failed to allocate string, len: %u", cch);
492
493 *ppsz = psz;
494 }
495
496 if (0 == ::WideCharToMultiByte(uiCodepage, 0, wzSource, 0 == cchSource ? -1 : (int)cchSource, *ppsz, (int)cch, NULL, NULL))
497 {
498 StrExitWithLastError(hr, "failed to convert to ansi: %ls", wzSource);
499 }
500 (*ppsz)[cchDest] = L'\0';
501
502LExit:
503 return hr;
504}
505
506
507/********************************************************************
508StrAllocStringAnsi - allocates or reuses dynamic string memory and copies in an existing ANSI string
509
510NOTE: caller is responsible for freeing ppwz even if function fails
511NOTE: cchSource must equal the length of wzSource (not including the NULL terminator)
512NOTE: if cchSource == 0, length of wzSource is used instead
513********************************************************************/
514extern "C" HRESULT DAPI StrAllocStringAnsi(
515 __deref_out_ecount_z(cchSource+1) LPWSTR* ppwz,
516 __in_z LPCSTR szSource,
517 __in SIZE_T cchSource,
518 __in UINT uiCodepage
519 )
520{
521 Assert(ppwz && szSource);
522
523 HRESULT hr = S_OK;
524 LPWSTR pwz = NULL;
525 SIZE_T cch = 0;
526 SIZE_T cchDest = cchSource; // at least enough
527
528 if (*ppwz)
529 {
530 cch = MemSize(*ppwz); // get the count in bytes so we can check if it failed (returns -1)
531 if (-1 == cch)
532 {
533 hr = E_INVALIDARG;
534 StrExitOnFailure(hr, "failed to get size of destination string");
535 }
536 cch /= sizeof(WCHAR); //convert the count in bytes to count in characters
537 }
538
539 if (0 == cchSource)
540 {
541 cchDest = ::MultiByteToWideChar(uiCodepage, 0, szSource, -1, NULL, 0);
542 if (0 == cchDest)
543 {
544 StrExitWithLastError(hr, "failed to get required size for conversion to unicode: %s", szSource);
545 }
546
547 --cchDest; //subtract one because MultiByteToWideChar includes space for the NULL terminator that we track below
548 }
549 else if (L'\0' == szSource[cchSource - 1]) // if the source already had a null terminator, don't count that in the character count because we track it below
550 {
551 cchDest = cchSource - 1;
552 }
553
554 if (cch < cchDest + 1)
555 {
556 cch = cchDest + 1;
557 if (cch >= MAXDWORD / sizeof(WCHAR))
558 {
559 hr = E_OUTOFMEMORY;
560 StrExitOnFailure(hr, "Not enough memory to allocate string of size: %u", cch);
561 }
562
563 if (*ppwz)
564 {
565 pwz = static_cast<LPWSTR>(MemReAlloc(*ppwz, sizeof(WCHAR) * cch, TRUE));
566 }
567 else
568 {
569 pwz = static_cast<LPWSTR>(MemAlloc(sizeof(WCHAR) * cch, TRUE));
570 }
571
572 StrExitOnNull(pwz, hr, E_OUTOFMEMORY, "failed to allocate string, len: %u", cch);
573
574 *ppwz = pwz;
575 }
576
577 if (0 == ::MultiByteToWideChar(uiCodepage, 0, szSource, 0 == cchSource ? -1 : (int)cchSource, *ppwz, (int)cch))
578 {
579 StrExitWithLastError(hr, "failed to convert to unicode: %s", szSource);
580 }
581 (*ppwz)[cchDest] = L'\0';
582
583LExit:
584 return hr;
585}
586
587
588/********************************************************************
589StrAnsiAllocStringAnsi - allocates or reuses dynamic string memory and copies in an existing string
590
591NOTE: caller is responsible for freeing ppsz even if function fails
592NOTE: cchSource does not have to equal the length of wzSource
593NOTE: if cchSource == 0, length of wzSource is used instead
594********************************************************************/
595HRESULT DAPI StrAnsiAllocStringAnsi(
596 __deref_out_ecount_z(cchSource+1) LPSTR* ppsz,
597 __in_z LPCSTR szSource,
598 __in SIZE_T cchSource
599 )
600{
601 Assert(ppsz && szSource); // && *szSource);
602
603 HRESULT hr = S_OK;
604 SIZE_T cch = 0;
605
606 if (*ppsz)
607 {
608 cch = MemSize(*ppsz); // get the count in bytes so we can check if it failed (returns -1)
609 if (-1 == cch)
610 {
611 hr = E_INVALIDARG;
612 StrExitOnRootFailure(hr, "failed to get size of destination string");
613 }
614 cch /= sizeof(CHAR); //convert the count in bytes to count in characters
615 }
616
617 if (0 == cchSource && szSource)
618 {
619 hr = ::StringCchLengthA(szSource, STRSAFE_MAX_CCH, reinterpret_cast<size_t*>(&cchSource));
620 StrExitOnRootFailure(hr, "failed to get length of source string");
621 }
622
623 SIZE_T cchNeeded;
624 hr = ::ULongPtrAdd(cchSource, 1, &cchNeeded); // add one for the null terminator
625 StrExitOnRootFailure(hr, "source string is too long");
626
627 if (cch < cchNeeded)
628 {
629 cch = cchNeeded;
630 hr = StrAnsiAlloc(ppsz, cch);
631 StrExitOnFailure(hr, "failed to allocate string from string.");
632 }
633
634 // copy everything (the NULL terminator will be included)
635#pragma prefast(push)
636#pragma prefast(disable:25068)
637 hr = ::StringCchCopyNExA(*ppsz, cch, szSource, cchSource, NULL, NULL, STRSAFE_FILL_BEHIND_NULL);
638#pragma prefast(pop)
639
640LExit:
641 return hr;
642}
643
644
645/********************************************************************
646StrAllocPrefix - allocates or reuses dynamic string memory and
647 prefixes a string
648
649NOTE: caller is responsible for freeing ppwz even if function fails
650NOTE: cchPrefix does not have to equal the length of wzPrefix
651NOTE: if cchPrefix == 0, length of wzPrefix is used instead
652********************************************************************/
653extern "C" HRESULT DAPI StrAllocPrefix(
654 __deref_out_z LPWSTR* ppwz,
655 __in_z LPCWSTR wzPrefix,
656 __in SIZE_T cchPrefix
657 )
658{
659 Assert(ppwz && wzPrefix);
660
661 HRESULT hr = S_OK;
662 SIZE_T cch = 0;
663 SIZE_T cchLen = 0;
664
665 if (*ppwz)
666 {
667 cch = MemSize(*ppwz); // get the count in bytes so we can check if it failed (returns -1)
668 if (-1 == cch)
669 {
670 hr = E_INVALIDARG;
671 StrExitOnFailure(hr, "failed to get size of destination string");
672 }
673 cch /= sizeof(WCHAR); //convert the count in bytes to count in characters
674
675 hr = ::StringCchLengthW(*ppwz, STRSAFE_MAX_CCH, reinterpret_cast<UINT_PTR*>(&cchLen));
676 StrExitOnFailure(hr, "Failed to calculate length of string");
677 }
678
679 Assert(cchLen <= cch);
680
681 if (0 == cchPrefix)
682 {
683 hr = ::StringCchLengthW(wzPrefix, STRSAFE_MAX_CCH, reinterpret_cast<UINT_PTR*>(&cchPrefix));
684 StrExitOnFailure(hr, "Failed to calculate length of string");
685 }
686
687 if (cch - cchLen < cchPrefix + 1)
688 {
689 cch = cchPrefix + cchLen + 1;
690 hr = StrAlloc(ppwz, cch);
691 StrExitOnFailure(hr, "failed to allocate string from string: %ls", wzPrefix);
692 }
693
694 if (*ppwz)
695 {
696 SIZE_T cb = cch * sizeof(WCHAR);
697 SIZE_T cbPrefix = cchPrefix * sizeof(WCHAR);
698
699 memmove(*ppwz + cchPrefix, *ppwz, cb - cbPrefix);
700 memcpy(*ppwz, wzPrefix, cbPrefix);
701 }
702 else
703 {
704 hr = E_UNEXPECTED;
705 StrExitOnFailure(hr, "for some reason our buffer is still null");
706 }
707
708LExit:
709 return hr;
710}
711
712
713/********************************************************************
714StrAllocConcat - allocates or reuses dynamic string memory and adds an existing string
715
716NOTE: caller is responsible for freeing ppwz even if function fails
717NOTE: cchSource does not have to equal the length of wzSource
718NOTE: if cchSource == 0, length of wzSource is used instead
719********************************************************************/
720extern "C" HRESULT DAPI StrAllocConcat(
721 __deref_out_z LPWSTR* ppwz,
722 __in_z LPCWSTR wzSource,
723 __in SIZE_T cchSource
724 )
725{
726 return AllocConcatHelper(ppwz, wzSource, cchSource, FALSE);
727}
728
729
730/********************************************************************
731StrAllocConcatSecure - allocates or reuses dynamic string memory and
732adds an existing string. If the memory needs to reallocated, calls
733SecureZeroMemory on the original block of memory after it is moved.
734
735NOTE: caller is responsible for freeing ppwz even if function fails
736NOTE: cchSource does not have to equal the length of wzSource
737NOTE: if cchSource == 0, length of wzSource is used instead
738********************************************************************/
739extern "C" HRESULT DAPI StrAllocConcatSecure(
740 __deref_out_z LPWSTR* ppwz,
741 __in_z LPCWSTR wzSource,
742 __in SIZE_T cchSource
743 )
744{
745 return AllocConcatHelper(ppwz, wzSource, cchSource, TRUE);
746}
747
748
749/********************************************************************
750AllocConcatHelper - allocates or reuses dynamic string memory and adds an existing string
751If fZeroOnRealloc is true and the memory needs to reallocated,
752calls SecureZeroMemory on original block of memory after it is moved.
753
754NOTE: caller is responsible for freeing ppwz even if function fails
755NOTE: cchSource does not have to equal the length of wzSource
756NOTE: if cchSource == 0, length of wzSource is used instead
757********************************************************************/
758static HRESULT AllocConcatHelper(
759 __deref_out_z LPWSTR* ppwz,
760 __in_z LPCWSTR wzSource,
761 __in SIZE_T cchSource,
762 __in BOOL fZeroOnRealloc
763 )
764{
765 Assert(ppwz && wzSource); // && *wzSource);
766
767 HRESULT hr = S_OK;
768 SIZE_T cch = 0;
769 SIZE_T cchLen = 0;
770
771 if (*ppwz)
772 {
773 cch = MemSize(*ppwz); // get the count in bytes so we can check if it failed (returns -1)
774 if (-1 == cch)
775 {
776 hr = E_INVALIDARG;
777 StrExitOnFailure(hr, "failed to get size of destination string");
778 }
779 cch /= sizeof(WCHAR); //convert the count in bytes to count in characters
780
781 hr = ::StringCchLengthW(*ppwz, STRSAFE_MAX_CCH, reinterpret_cast<UINT_PTR*>(&cchLen));
782 StrExitOnFailure(hr, "Failed to calculate length of string");
783 }
784
785 Assert(cchLen <= cch);
786
787 if (0 == cchSource)
788 {
789 hr = ::StringCchLengthW(wzSource, STRSAFE_MAX_CCH, reinterpret_cast<UINT_PTR*>(&cchSource));
790 StrExitOnFailure(hr, "Failed to calculate length of string");
791 }
792
793 if (cch - cchLen < cchSource + 1)
794 {
795 cch = (cchSource + cchLen + 1) * 2;
796 hr = AllocHelper(ppwz, cch, fZeroOnRealloc);
797 StrExitOnFailure(hr, "failed to allocate string from string: %ls", wzSource);
798 }
799
800 if (*ppwz)
801 {
802 hr = ::StringCchCatNExW(*ppwz, cch, wzSource, cchSource, NULL, NULL, STRSAFE_FILL_BEHIND_NULL);
803 }
804 else
805 {
806 hr = E_UNEXPECTED;
807 StrExitOnFailure(hr, "for some reason our buffer is still null");
808 }
809
810LExit:
811 return hr;
812}
813
814
815/********************************************************************
816StrAnsiAllocConcat - allocates or reuses dynamic string memory and adds an existing string
817
818NOTE: caller is responsible for freeing ppz even if function fails
819NOTE: cchSource does not have to equal the length of pzSource
820NOTE: if cchSource == 0, length of pzSource is used instead
821********************************************************************/
822extern "C" HRESULT DAPI StrAnsiAllocConcat(
823 __deref_out_z LPSTR* ppz,
824 __in_z LPCSTR pzSource,
825 __in SIZE_T cchSource
826 )
827{
828 Assert(ppz && pzSource); // && *pzSource);
829
830 HRESULT hr = S_OK;
831 SIZE_T cch = 0;
832 SIZE_T cchLen = 0;
833
834 if (*ppz)
835 {
836 cch = MemSize(*ppz); // get the count in bytes so we can check if it failed (returns -1)
837 if (-1 == cch)
838 {
839 hr = E_INVALIDARG;
840 StrExitOnFailure(hr, "failed to get size of destination string");
841 }
842 cch /= sizeof(CHAR); // convert the count in bytes to count in characters
843
844#pragma prefast(push)
845#pragma prefast(disable:25068)
846 hr = ::StringCchLengthA(*ppz, STRSAFE_MAX_CCH, reinterpret_cast<UINT_PTR*>(&cchLen));
847#pragma prefast(pop)
848 StrExitOnFailure(hr, "Failed to calculate length of string");
849 }
850
851 Assert(cchLen <= cch);
852
853 if (0 == cchSource)
854 {
855#pragma prefast(push)
856#pragma prefast(disable:25068)
857 hr = ::StringCchLengthA(pzSource, STRSAFE_MAX_CCH, reinterpret_cast<UINT_PTR*>(&cchSource));
858#pragma prefast(pop)
859 StrExitOnFailure(hr, "Failed to calculate length of string");
860 }
861
862 if (cch - cchLen < cchSource + 1)
863 {
864 cch = (cchSource + cchLen + 1) * 2;
865 hr = StrAnsiAlloc(ppz, cch);
866 StrExitOnFailure(hr, "failed to allocate string from string: %hs", pzSource);
867 }
868
869 if (*ppz)
870 {
871#pragma prefast(push)
872#pragma prefast(disable:25068)
873 hr = ::StringCchCatNExA(*ppz, cch, pzSource, cchSource, NULL, NULL, STRSAFE_FILL_BEHIND_NULL);
874#pragma prefast(pop)
875 }
876 else
877 {
878 hr = E_UNEXPECTED;
879 StrExitOnFailure(hr, "for some reason our buffer is still null");
880 }
881
882LExit:
883 return hr;
884}
885
886
887/********************************************************************
888StrAllocFormatted - allocates or reuses dynamic string memory and formats it
889
890NOTE: caller is responsible for freeing ppwz even if function fails
891********************************************************************/
892extern "C" HRESULT __cdecl StrAllocFormatted(
893 __deref_out_z LPWSTR* ppwz,
894 __in __format_string LPCWSTR wzFormat,
895 ...
896 )
897{
898 Assert(ppwz && wzFormat && *wzFormat);
899
900 HRESULT hr = S_OK;
901 va_list args;
902
903 va_start(args, wzFormat);
904 hr = StrAllocFormattedArgs(ppwz, wzFormat, args);
905 va_end(args);
906
907 return hr;
908}
909
910
911/********************************************************************
912StrAllocConcatFormatted - allocates or reuses dynamic string memory
913and adds a formatted string
914
915NOTE: caller is responsible for freeing ppwz even if function fails
916********************************************************************/
917extern "C" HRESULT __cdecl StrAllocConcatFormatted(
918 __deref_out_z LPWSTR* ppwz,
919 __in __format_string LPCWSTR wzFormat,
920 ...
921 )
922{
923 Assert(ppwz && wzFormat && *wzFormat);
924
925 HRESULT hr = S_OK;
926 LPWSTR sczFormatted = NULL;
927 va_list args;
928
929 va_start(args, wzFormat);
930 hr = StrAllocFormattedArgs(&sczFormatted, wzFormat, args);
931 va_end(args);
932 StrExitOnFailure(hr, "Failed to allocate formatted string");
933
934 hr = StrAllocConcat(ppwz, sczFormatted, 0);
935
936LExit:
937 ReleaseStr(sczFormatted);
938
939 return hr;
940}
941
942
943/********************************************************************
944StrAllocConcatFormattedSecure - allocates or reuses dynamic string
945memory and adds a formatted string. If the memory needs to be
946reallocated, calls SecureZeroMemory on original block of memory after
947it is moved.
948
949NOTE: caller is responsible for freeing ppwz even if function fails
950********************************************************************/
951extern "C" HRESULT __cdecl StrAllocConcatFormattedSecure(
952 __deref_out_z LPWSTR* ppwz,
953 __in __format_string LPCWSTR wzFormat,
954 ...
955 )
956{
957 Assert(ppwz && wzFormat && *wzFormat);
958
959 HRESULT hr = S_OK;
960 LPWSTR sczFormatted = NULL;
961 va_list args;
962
963 va_start(args, wzFormat);
964 hr = StrAllocFormattedArgsSecure(&sczFormatted, wzFormat, args);
965 va_end(args);
966 StrExitOnFailure(hr, "Failed to allocate formatted string");
967
968 hr = StrAllocConcatSecure(ppwz, sczFormatted, 0);
969
970LExit:
971 ReleaseStr(sczFormatted);
972
973 return hr;
974}
975
976
977/********************************************************************
978StrAllocFormattedSecure - allocates or reuses dynamic string memory
979and formats it. If the memory needs to be reallocated,
980calls SecureZeroMemory on original block of memory after it is moved.
981
982NOTE: caller is responsible for freeing ppwz even if function fails
983********************************************************************/
984extern "C" HRESULT __cdecl StrAllocFormattedSecure(
985 __deref_out_z LPWSTR* ppwz,
986 __in __format_string LPCWSTR wzFormat,
987 ...
988 )
989{
990 Assert(ppwz && wzFormat && *wzFormat);
991
992 HRESULT hr = S_OK;
993 va_list args;
994
995 va_start(args, wzFormat);
996 hr = StrAllocFormattedArgsSecure(ppwz, wzFormat, args);
997 va_end(args);
998
999 return hr;
1000}
1001
1002
1003/********************************************************************
1004StrAnsiAllocFormatted - allocates or reuses dynamic ANSI string memory and formats it
1005
1006NOTE: caller is responsible for freeing ppsz even if function fails
1007********************************************************************/
1008extern "C" HRESULT DAPI StrAnsiAllocFormatted(
1009 __deref_out_z LPSTR* ppsz,
1010 __in __format_string LPCSTR szFormat,
1011 ...
1012 )
1013{
1014 Assert(ppsz && szFormat && *szFormat);
1015
1016 HRESULT hr = S_OK;
1017 va_list args;
1018
1019 va_start(args, szFormat);
1020 hr = StrAnsiAllocFormattedArgs(ppsz, szFormat, args);
1021 va_end(args);
1022
1023 return hr;
1024}
1025
1026
1027/********************************************************************
1028StrAllocFormattedArgs - allocates or reuses dynamic string memory
1029and formats it with the passed in args
1030
1031NOTE: caller is responsible for freeing ppwz even if function fails
1032********************************************************************/
1033extern "C" HRESULT DAPI StrAllocFormattedArgs(
1034 __deref_out_z LPWSTR* ppwz,
1035 __in __format_string LPCWSTR wzFormat,
1036 __in va_list args
1037 )
1038{
1039 return AllocFormattedArgsHelper(ppwz, FALSE, wzFormat, args);
1040}
1041
1042
1043/********************************************************************
1044StrAllocFormattedArgsSecure - allocates or reuses dynamic string memory
1045and formats it with the passed in args.
1046
1047If the memory needs to reallocated, calls SecureZeroMemory on the
1048original block of memory after it is moved.
1049
1050NOTE: caller is responsible for freeing ppwz even if function fails
1051********************************************************************/
1052extern "C" HRESULT DAPI StrAllocFormattedArgsSecure(
1053 __deref_out_z LPWSTR* ppwz,
1054 __in __format_string LPCWSTR wzFormat,
1055 __in va_list args
1056 )
1057{
1058 return AllocFormattedArgsHelper(ppwz, TRUE, wzFormat, args);
1059}
1060
1061
1062/********************************************************************
1063AllocFormattedArgsHelper - allocates or reuses dynamic string memory
1064and formats it with the passed in args.
1065
1066If fZeroOnRealloc is true and the memory needs to reallocated,
1067calls SecureZeroMemory on original block of memory after it is moved.
1068
1069NOTE: caller is responsible for freeing ppwz even if function fails
1070********************************************************************/
1071static HRESULT AllocFormattedArgsHelper(
1072 __deref_out_z LPWSTR* ppwz,
1073 __in BOOL fZeroOnRealloc,
1074 __in __format_string LPCWSTR wzFormat,
1075 __in va_list args
1076 )
1077{
1078 Assert(ppwz && wzFormat && *wzFormat);
1079
1080 HRESULT hr = S_OK;
1081 SIZE_T cch = 0;
1082 LPWSTR pwzOriginal = NULL;
1083 SIZE_T cbOriginal = 0;
1084 size_t cchOriginal = 0;
1085
1086 if (*ppwz)
1087 {
1088 cbOriginal = MemSize(*ppwz); // get the count in bytes so we can check if it failed (returns -1)
1089 if (-1 == cbOriginal)
1090 {
1091 hr = E_INVALIDARG;
1092 StrExitOnRootFailure(hr, "failed to get size of destination string");
1093 }
1094
1095 cch = cbOriginal / sizeof(WCHAR); //convert the count in bytes to count in characters
1096
1097 hr = ::StringCchLengthW(*ppwz, STRSAFE_MAX_CCH, &cchOriginal);
1098 StrExitOnRootFailure(hr, "failed to get length of original string");
1099 }
1100
1101 if (0 == cch) // if there is no space in the string buffer
1102 {
1103 cch = 256;
1104
1105 hr = AllocHelper(ppwz, cch, fZeroOnRealloc);
1106 StrExitOnFailure(hr, "failed to allocate string to format: %ls", wzFormat);
1107 }
1108
1109 // format the message (grow until it fits or there is a failure)
1110 do
1111 {
1112 hr = ::StringCchVPrintfW(*ppwz, cch, wzFormat, args);
1113 if (STRSAFE_E_INSUFFICIENT_BUFFER == hr)
1114 {
1115 if (!pwzOriginal)
1116 {
1117 // this allows you to pass the original string as a formatting argument and not crash
1118 // save the original string and free it after the printf is complete
1119 pwzOriginal = *ppwz;
1120 *ppwz = NULL;
1121
1122 // StringCchVPrintfW starts writing to the string...
1123 // NOTE: this hack only works with sprintf(&pwz, "%s ...", pwz, ...);
1124 pwzOriginal[cchOriginal] = 0;
1125 }
1126
1127 cch *= 2;
1128
1129 hr = AllocHelper(ppwz, cch, fZeroOnRealloc);
1130 StrExitOnFailure(hr, "failed to allocate string to format: %ls", wzFormat);
1131
1132 hr = S_FALSE;
1133 }
1134 } while (S_FALSE == hr);
1135 StrExitOnRootFailure(hr, "failed to format string");
1136
1137LExit:
1138 if (pwzOriginal && fZeroOnRealloc)
1139 {
1140 SecureZeroMemory(pwzOriginal, cbOriginal);
1141 }
1142
1143 ReleaseStr(pwzOriginal);
1144
1145 return hr;
1146}
1147
1148
1149/********************************************************************
1150StrAnsiAllocFormattedArgs - allocates or reuses dynamic ANSI string memory
1151and formats it with the passed in args
1152
1153NOTE: caller is responsible for freeing ppsz even if function fails
1154********************************************************************/
1155extern "C" HRESULT DAPI StrAnsiAllocFormattedArgs(
1156 __deref_out_z LPSTR* ppsz,
1157 __in __format_string LPCSTR szFormat,
1158 __in va_list args
1159 )
1160{
1161 Assert(ppsz && szFormat && *szFormat);
1162
1163 HRESULT hr = S_OK;
1164 SIZE_T cch = *ppsz ? MemSize(*ppsz) / sizeof(CHAR) : 0;
1165 LPSTR pszOriginal = NULL;
1166 size_t cchOriginal = 0;
1167
1168 if (*ppsz)
1169 {
1170 cch = MemSize(*ppsz); // get the count in bytes so we can check if it failed (returns -1)
1171 if (-1 == cch)
1172 {
1173 hr = E_INVALIDARG;
1174 StrExitOnRootFailure(hr, "failed to get size of destination string");
1175 }
1176 cch /= sizeof(CHAR); //convert the count in bytes to count in characters
1177
1178 hr = ::StringCchLengthA(*ppsz, STRSAFE_MAX_CCH, &cchOriginal);
1179 StrExitOnRootFailure(hr, "failed to get length of original string");
1180 }
1181
1182 if (0 == cch) // if there is no space in the string buffer
1183 {
1184 cch = 256;
1185 hr = StrAnsiAlloc(ppsz, cch);
1186 StrExitOnFailure(hr, "failed to allocate string to format: %s", szFormat);
1187 }
1188
1189 // format the message (grow until it fits or there is a failure)
1190 do
1191 {
1192#pragma prefast(push)
1193#pragma prefast(disable:25068) // We intentionally don't use the unicode API here
1194 hr = ::StringCchVPrintfA(*ppsz, cch, szFormat, args);
1195#pragma prefast(pop)
1196 if (STRSAFE_E_INSUFFICIENT_BUFFER == hr)
1197 {
1198 if (!pszOriginal)
1199 {
1200 // this allows you to pass the original string as a formatting argument and not crash
1201 // save the original string and free it after the printf is complete
1202 pszOriginal = *ppsz;
1203 *ppsz = NULL;
1204 // StringCchVPrintfW starts writing to the string...
1205 // NOTE: this hack only works with sprintf(&pwz, "%s ...", pwz, ...);
1206 pszOriginal[cchOriginal] = 0;
1207 }
1208 cch *= 2;
1209 hr = StrAnsiAlloc(ppsz, cch);
1210 StrExitOnFailure(hr, "failed to allocate string to format: %hs", szFormat);
1211 hr = S_FALSE;
1212 }
1213 } while (S_FALSE == hr);
1214 StrExitOnRootFailure(hr, "failed to format string");
1215
1216LExit:
1217 ReleaseStr(pszOriginal);
1218
1219 return hr;
1220}
1221
1222
1223/********************************************************************
1224StrAllocFromError - returns the string for a particular error.
1225
1226********************************************************************/
1227extern "C" HRESULT DAPI StrAllocFromError(
1228 __inout LPWSTR *ppwzMessage,
1229 __in HRESULT hrError,
1230 __in_opt HMODULE hModule,
1231 ...
1232 )
1233{
1234 HRESULT hr = S_OK;
1235 DWORD dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_MAX_WIDTH_MASK | FORMAT_MESSAGE_FROM_SYSTEM;
1236 LPVOID pvMessage = NULL;
1237 DWORD cchMessage = 0;
1238
1239 if (hModule)
1240 {
1241 dwFlags |= FORMAT_MESSAGE_FROM_HMODULE;
1242 }
1243
1244 va_list args;
1245 va_start(args, hModule);
1246 cchMessage = ::FormatMessageW(dwFlags, static_cast<LPCVOID>(hModule), hrError, 0, reinterpret_cast<LPWSTR>(&pvMessage), 0, &args);
1247 va_end(args);
1248
1249 if (0 == cchMessage)
1250 {
1251 StrExitWithLastError(hr, "Failed to format message for error: 0x%x", hrError);
1252 }
1253
1254 hr = StrAllocString(ppwzMessage, reinterpret_cast<LPCWSTR>(pvMessage), cchMessage);
1255 StrExitOnFailure(hr, "Failed to allocate string for message.");
1256
1257LExit:
1258 if (pvMessage)
1259 {
1260 ::LocalFree(pvMessage);
1261 }
1262
1263 return hr;
1264}
1265
1266
1267/********************************************************************
1268StrMaxLength - returns maximum number of characters that can be stored in dynamic string p
1269
1270NOTE: assumes Unicode string
1271********************************************************************/
1272extern "C" HRESULT DAPI StrMaxLength(
1273 __in LPCVOID p,
1274 __out SIZE_T* pcch
1275 )
1276{
1277 Assert(pcch);
1278
1279 HRESULT hr = S_OK;
1280
1281 if (p)
1282 {
1283 *pcch = MemSize(p); // get size of entire buffer
1284 if (-1 == *pcch)
1285 {
1286 ExitFunction1(hr = E_FAIL);
1287 }
1288
1289 *pcch /= sizeof(WCHAR); // reduce to count of characters
1290 }
1291 else
1292 {
1293 *pcch = 0;
1294 }
1295 Assert(S_OK == hr);
1296
1297LExit:
1298 return hr;
1299}
1300
1301
1302/********************************************************************
1303StrSize - returns count of bytes in dynamic string p
1304
1305********************************************************************/
1306extern "C" HRESULT DAPI StrSize(
1307 __in LPCVOID p,
1308 __out SIZE_T* pcb
1309 )
1310{
1311 Assert(p && pcb);
1312
1313 HRESULT hr = S_OK;
1314
1315 *pcb = MemSize(p);
1316 if (-1 == *pcb)
1317 {
1318 hr = E_FAIL;
1319 }
1320
1321 return hr;
1322}
1323
1324/********************************************************************
1325StrFree - releases dynamic string memory allocated by any StrAlloc*() functions
1326
1327********************************************************************/
1328extern "C" HRESULT DAPI StrFree(
1329 __in LPVOID p
1330 )
1331{
1332 Assert(p);
1333
1334 HRESULT hr = MemFree(p);
1335 StrExitOnFailure(hr, "failed to free string");
1336
1337LExit:
1338 return hr;
1339}
1340
1341
1342/****************************************************************************
1343StrReplaceStringAll - Replaces wzOldSubString in ppOriginal with a wzNewSubString.
1344Replaces all instances.
1345
1346****************************************************************************/
1347extern "C" HRESULT DAPI StrReplaceStringAll(
1348 __inout LPWSTR* ppwzOriginal,
1349 __in_z LPCWSTR wzOldSubString,
1350 __in_z LPCWSTR wzNewSubString
1351 )
1352{
1353 HRESULT hr = S_OK;
1354 DWORD dwStartIndex = 0;
1355
1356 do
1357 {
1358 hr = StrReplaceString(ppwzOriginal, &dwStartIndex, wzOldSubString, wzNewSubString);
1359 StrExitOnFailure(hr, "Failed to replace substring");
1360 }
1361 while (S_OK == hr);
1362
1363 hr = (0 == dwStartIndex) ? S_FALSE : S_OK;
1364
1365LExit:
1366 return hr;
1367}
1368
1369
1370/****************************************************************************
1371StrReplaceString - Replaces wzOldSubString in ppOriginal with a wzNewSubString.
1372Search for old substring starts at dwStartIndex. Does only 1 replace.
1373
1374****************************************************************************/
1375extern "C" HRESULT DAPI StrReplaceString(
1376 __inout LPWSTR* ppwzOriginal,
1377 __inout DWORD* pdwStartIndex,
1378 __in_z LPCWSTR wzOldSubString,
1379 __in_z LPCWSTR wzNewSubString
1380 )
1381{
1382 Assert(ppwzOriginal && wzOldSubString && wzNewSubString);
1383
1384 HRESULT hr = S_FALSE;
1385 LPCWSTR wzSubLocation = NULL;
1386 LPWSTR pwzBuffer = NULL;
1387 size_t cchOldSubString = 0;
1388 size_t cchNewSubString = 0;
1389
1390 if (!*ppwzOriginal)
1391 {
1392 ExitFunction();
1393 }
1394
1395 wzSubLocation = wcsstr(*ppwzOriginal + *pdwStartIndex, wzOldSubString);
1396 if (!wzSubLocation)
1397 {
1398 ExitFunction();
1399 }
1400
1401 if (wzOldSubString)
1402 {
1403 hr = ::StringCchLengthW(wzOldSubString, STRSAFE_MAX_CCH, &cchOldSubString);
1404 StrExitOnRootFailure(hr, "Failed to get old string length.");
1405 }
1406
1407 if (wzNewSubString)
1408 {
1409 hr = ::StringCchLengthW(wzNewSubString, STRSAFE_MAX_CCH, &cchNewSubString);
1410 StrExitOnRootFailure(hr, "Failed to get new string length.");
1411 }
1412
1413 hr = ::PtrdiffTToDWord(wzSubLocation - *ppwzOriginal, pdwStartIndex);
1414 StrExitOnRootFailure(hr, "Failed to diff pointers.");
1415
1416 hr = StrAllocString(&pwzBuffer, *ppwzOriginal, wzSubLocation - *ppwzOriginal);
1417 StrExitOnFailure(hr, "Failed to duplicate string.");
1418
1419 pwzBuffer[wzSubLocation - *ppwzOriginal] = '\0';
1420
1421 hr = StrAllocConcat(&pwzBuffer, wzNewSubString, 0);
1422 StrExitOnFailure(hr, "Failed to append new string.");
1423
1424 hr = StrAllocConcat(&pwzBuffer, wzSubLocation + cchOldSubString, 0);
1425 StrExitOnFailure(hr, "Failed to append post string.");
1426
1427 hr = StrFree(*ppwzOriginal);
1428 StrExitOnFailure(hr, "Failed to free original string.");
1429
1430 *ppwzOriginal = pwzBuffer;
1431 *pdwStartIndex = *pdwStartIndex + static_cast<DWORD>(cchNewSubString);
1432 hr = S_OK;
1433
1434LExit:
1435 return hr;
1436}
1437
1438
1439static inline BYTE HexCharToByte(
1440 __in WCHAR wc
1441 )
1442{
1443 Assert(L'0' <= wc && wc <= L'9' || L'a' <= wc && wc <= L'f' || L'A' <= wc && wc <= L'F'); // make sure wc is a hex character
1444
1445 BYTE b;
1446 if (L'0' <= wc && wc <= L'9')
1447 {
1448 b = (BYTE)(wc - L'0');
1449 }
1450 else if ('a' <= wc && wc <= 'f')
1451 {
1452 b = (BYTE)(wc - L'0' - (L'a' - L'9' - 1));
1453 }
1454 else // must be (L'A' <= wc && wc <= L'F')
1455 {
1456 b = (BYTE)(wc - L'0' - (L'A' - L'9' - 1));
1457 }
1458
1459 Assert(0 <= b && b <= 15);
1460 return b;
1461}
1462
1463
1464/****************************************************************************
1465StrHexEncode - converts an array of bytes to a text string
1466
1467NOTE: wzDest must have space for cbSource * 2 + 1 characters
1468****************************************************************************/
1469extern "C" HRESULT DAPI StrHexEncode(
1470 __in_ecount(cbSource) const BYTE* pbSource,
1471 __in SIZE_T cbSource,
1472 __out_ecount(cchDest) LPWSTR wzDest,
1473 __in SIZE_T cchDest
1474 )
1475{
1476 Assert(pbSource && wzDest);
1477
1478 HRESULT hr = S_OK;
1479 DWORD i;
1480 BYTE b;
1481
1482 if (cchDest < 2 * cbSource + 1)
1483 {
1484 ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER));
1485 }
1486
1487 for (i = 0; i < cbSource; ++i)
1488 {
1489 b = (*pbSource) >> 4;
1490 *(wzDest++) = (WCHAR)(L'0' + b + ((b < 10) ? 0 : L'A'-L'9'-1));
1491 b = (*pbSource) & 0xF;
1492 *(wzDest++) = (WCHAR)(L'0' + b + ((b < 10) ? 0 : L'A'-L'9'-1));
1493
1494 ++pbSource;
1495 }
1496
1497 *wzDest = 0;
1498
1499LExit:
1500 return hr;
1501}
1502
1503
1504/****************************************************************************
1505StrAllocHexEncode - converts an array of bytes to an allocated text string
1506
1507****************************************************************************/
1508HRESULT DAPI StrAllocHexEncode(
1509 __in_ecount(cbSource) const BYTE* pbSource,
1510 __in SIZE_T cbSource,
1511 __deref_out_ecount_z(2*(cbSource+1)) LPWSTR* ppwzDest
1512 )
1513{
1514 HRESULT hr = S_OK;
1515 SIZE_T cchSource = sizeof(WCHAR) * (cbSource + 1);
1516
1517 hr = StrAlloc(ppwzDest, cchSource);
1518 StrExitOnFailure(hr, "Failed to allocate hex string.");
1519
1520 hr = StrHexEncode(pbSource, cbSource, *ppwzDest, cchSource);
1521 StrExitOnFailure(hr, "Failed to encode hex string.");
1522
1523LExit:
1524 return hr;
1525}
1526
1527
1528/****************************************************************************
1529StrHexDecode - converts a string of text to array of bytes
1530
1531NOTE: wzSource must contain even number of characters
1532****************************************************************************/
1533extern "C" HRESULT DAPI StrHexDecode(
1534 __in_z LPCWSTR wzSource,
1535 __out_bcount(cbDest) BYTE* pbDest,
1536 __in SIZE_T cbDest
1537 )
1538{
1539 Assert(wzSource && pbDest);
1540
1541 HRESULT hr = S_OK;
1542 size_t cchSource = 0;
1543 size_t i = 0;
1544 BYTE b = 0;
1545
1546 hr = ::StringCchLengthW(wzSource, STRSAFE_MAX_CCH, &cchSource);
1547 StrExitOnRootFailure(hr, "Failed to get length of hex string: %ls", wzSource);
1548
1549 Assert(0 == cchSource % 2);
1550 if (cbDest < cchSource / 2)
1551 {
1552 hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
1553 StrExitOnRootFailure(hr, "Insufficient buffer to decode string '%ls' len: %Iu into %Iu bytes.", wzSource, cchSource, cbDest);
1554 }
1555
1556 for (i = 0; i < cchSource / 2; ++i)
1557 {
1558 b = HexCharToByte(*wzSource++);
1559 (*pbDest) = b << 4;
1560
1561 b = HexCharToByte(*wzSource++);
1562 (*pbDest) |= b & 0xF;
1563
1564 ++pbDest;
1565 }
1566
1567LExit:
1568 return hr;
1569}
1570
1571
1572/****************************************************************************
1573StrAllocHexDecode - allocates a byte array hex-converted from string of text
1574
1575NOTE: wzSource must contain even number of characters
1576****************************************************************************/
1577extern "C" HRESULT DAPI StrAllocHexDecode(
1578 __in_z LPCWSTR wzSource,
1579 __out_bcount(*pcbDest) BYTE** ppbDest,
1580 __out_opt DWORD* pcbDest
1581 )
1582{
1583 Assert(wzSource && *wzSource && ppbDest);
1584
1585 HRESULT hr = S_OK;
1586 size_t cch = 0;
1587 BYTE* pb = NULL;
1588 DWORD cb = 0;
1589
1590 hr = ::StringCchLengthW(wzSource, STRSAFE_MAX_CCH, &cch);
1591 StrExitOnFailure(hr, "Failed to calculate length of source string.");
1592
1593 if (cch % 2)
1594 {
1595 hr = E_INVALIDARG;
1596 StrExitOnFailure(hr, "Invalid source parameter, string must be even length or it cannot be decoded.");
1597 }
1598
1599 cb = static_cast<DWORD>(cch / 2);
1600 pb = static_cast<BYTE*>(MemAlloc(cb, TRUE));
1601 StrExitOnNull(pb, hr, E_OUTOFMEMORY, "Failed to allocate memory for hex decode.");
1602
1603 hr = StrHexDecode(wzSource, pb, cb);
1604 StrExitOnFailure(hr, "Failed to decode source string.");
1605
1606 *ppbDest = pb;
1607 pb = NULL;
1608
1609 if (pcbDest)
1610 {
1611 *pcbDest = cb;
1612 }
1613
1614LExit:
1615 ReleaseMem(pb);
1616
1617 return hr;
1618}
1619
1620
1621/****************************************************************************
1622Base85 encoding/decoding data
1623
1624****************************************************************************/
1625const WCHAR Base85EncodeTable[] = L"!%'()*+,-./0123456789:;?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_abcdefghijklmnopqrstuvwxyz{|}~";
1626
1627const BYTE Base85DecodeTable[256] =
1628{
1629 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
1630 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
1631 85, 0, 85, 85, 85, 1, 85, 2, 3, 4, 5, 6, 7, 8, 9, 10,
1632 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 85, 85, 85, 23,
1633 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
1634 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 85, 52, 53, 54,
1635 85, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
1636 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85,
1637 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
1638 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
1639 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
1640 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
1641 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
1642 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
1643 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
1644 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85
1645};
1646
1647const UINT Base85PowerTable[4] = { 1, 85, 85*85, 85*85*85 };
1648
1649
1650/****************************************************************************
1651StrAllocBase85Encode - converts an array of bytes into an XML compatible string
1652
1653****************************************************************************/
1654extern "C" HRESULT DAPI StrAllocBase85Encode(
1655 __in_bcount_opt(cbSource) const BYTE* pbSource,
1656 __in SIZE_T cbSource,
1657 __deref_out_z LPWSTR* pwzDest
1658 )
1659{
1660 HRESULT hr = S_OK;
1661 SIZE_T cchDest = 0;
1662 LPWSTR wzDest;
1663 DWORD_PTR iSource = 0;
1664 DWORD_PTR iDest = 0;
1665
1666 if (!pwzDest || !pbSource)
1667 {
1668 return E_INVALIDARG;
1669 }
1670
1671 // calc actual size of output
1672 cchDest = cbSource / 4;
1673 cchDest += cchDest * 4;
1674 if (cbSource & 3)
1675 {
1676 cchDest += (cbSource & 3) + 1;
1677 }
1678 ++cchDest; // add room for null terminator
1679
1680 hr = StrAlloc(pwzDest, cchDest);
1681 StrExitOnFailure(hr, "failed to allocate destination string");
1682
1683 wzDest = *pwzDest;
1684
1685 // first, encode full words
1686 for (iSource = 0, iDest = 0; (iSource + 4 < cbSource) && (iDest + 5 < cchDest); iSource += 4, iDest += 5)
1687 {
1688 DWORD n = pbSource[iSource] + (pbSource[iSource + 1] << 8) + (pbSource[iSource + 2] << 16) + (pbSource[iSource + 3] << 24);
1689 DWORD k = n / 85;
1690
1691 //Assert(0 <= (n - k * 85) && (n - k * 85) < countof(Base85EncodeTable));
1692 wzDest[iDest] = Base85EncodeTable[n - k * 85];
1693 n = k / 85;
1694
1695 //Assert(0 <= (k - n * 85) && (k - n * 85) < countof(Base85EncodeTable));
1696 wzDest[iDest + 1] = Base85EncodeTable[k - n * 85];
1697 k = n / 85;
1698
1699 //Assert(0 <= (n - k * 85) && (n - k * 85) < countof(Base85EncodeTable));
1700 wzDest[iDest + 2] = Base85EncodeTable[n - k * 85];
1701 n = k / 85;
1702
1703 //Assert(0 <= (k - n * 85) && (k - n * 85) < countof(Base85EncodeTable));
1704 wzDest[iDest + 3] = Base85EncodeTable[k - n * 85];
1705
1706 __assume(n <= DWORD_MAX / 85 / 85 / 85 / 85);
1707
1708 //Assert(0 <= n && n < countof(Base85EncodeTable));
1709 wzDest[iDest + 4] = Base85EncodeTable[n];
1710 }
1711
1712 // encode any remaining bytes
1713 if (iSource < cbSource)
1714 {
1715 DWORD n = 0;
1716 for (DWORD i = 0; iSource + i < cbSource; ++i)
1717 {
1718 n += pbSource[iSource + i] << (i << 3);
1719 }
1720
1721 for (/* iSource already initialized */; iSource < cbSource && iDest < cchDest; ++iSource, ++iDest)
1722 {
1723 DWORD k = n / 85;
1724
1725 //Assert(0 <= (n - k * 85) && (n - k * 85) < countof(Base85EncodeTable));
1726 wzDest[iDest] = Base85EncodeTable[n - k * 85];
1727
1728 n = k;
1729 }
1730
1731 wzDest[iDest] = Base85EncodeTable[n];
1732 ++iDest;
1733 }
1734 Assert(iSource == cbSource);
1735 Assert(iDest == cchDest - 1);
1736
1737 wzDest[iDest] = L'\0';
1738 hr = S_OK;
1739
1740LExit:
1741 return hr;
1742}
1743
1744
1745/****************************************************************************
1746StrAllocBase85Decode - converts a string of text to array of bytes
1747
1748NOTE: Use MemFree() to release the allocated stream of bytes
1749****************************************************************************/
1750extern "C" HRESULT DAPI StrAllocBase85Decode(
1751 __in_z LPCWSTR wzSource,
1752 __deref_out_bcount(*pcbDest) BYTE** ppbDest,
1753 __out SIZE_T* pcbDest
1754 )
1755{
1756 HRESULT hr = S_OK;
1757 size_t cchSource = 0;
1758 DWORD_PTR i, n, k;
1759
1760 BYTE* pbDest = 0;
1761 SIZE_T cbDest = 0;
1762
1763 if (!wzSource || !ppbDest || !pcbDest)
1764 {
1765 ExitFunction1(hr = E_INVALIDARG);
1766 }
1767
1768 hr = ::StringCchLengthW(wzSource, STRSAFE_MAX_CCH, &cchSource);
1769 StrExitOnRootFailure(hr, "failed to get length of base 85 string: %ls", wzSource);
1770
1771 // evaluate size of output and check it
1772 k = cchSource / 5;
1773 cbDest = k << 2;
1774 k = cchSource - k * 5;
1775 if (k)
1776 {
1777 if (1 == k)
1778 {
1779 // decode error -- encoded size cannot equal 1 mod 5
1780 return E_UNEXPECTED;
1781 }
1782
1783 cbDest += k - 1;
1784 }
1785
1786 *ppbDest = static_cast<BYTE*>(MemAlloc(cbDest, FALSE));
1787 StrExitOnNull(*ppbDest, hr, E_OUTOFMEMORY, "failed allocate memory to decode the string");
1788
1789 pbDest = *ppbDest;
1790 *pcbDest = cbDest;
1791
1792 // decode full words first
1793 while (5 <= cchSource)
1794 {
1795 k = Base85DecodeTable[wzSource[0]];
1796 if (85 == k)
1797 {
1798 // illegal symbol
1799 return E_UNEXPECTED;
1800 }
1801 n = k;
1802
1803 k = Base85DecodeTable[wzSource[1]];
1804 if (85 == k)
1805 {
1806 // illegal symbol
1807 return E_UNEXPECTED;
1808 }
1809 n += k * 85;
1810
1811 k = Base85DecodeTable[wzSource[2]];
1812 if (85 == k)
1813 {
1814 // illegal symbol
1815 return E_UNEXPECTED;
1816 }
1817 n += k * (85 * 85);
1818
1819 k = Base85DecodeTable[wzSource[3]];
1820 if (85 == k)
1821 {
1822 // illegal symbol
1823 return E_UNEXPECTED;
1824 }
1825 n += k * (85 * 85 * 85);
1826
1827 k = Base85DecodeTable[wzSource[4]];
1828 if (85 == k)
1829 {
1830 // illegal symbol
1831 return E_UNEXPECTED;
1832 }
1833 k *= (85 * 85 * 85 * 85);
1834
1835 // if (k + n > (1u << 32)) <=> (k > ~n) then decode error
1836 if (k > ~n)
1837 {
1838 // overflow
1839 return E_UNEXPECTED;
1840 }
1841
1842 n += k;
1843
1844 pbDest[0] = (BYTE) n;
1845 pbDest[1] = (BYTE) (n >> 8);
1846 pbDest[2] = (BYTE) (n >> 16);
1847 pbDest[3] = (BYTE) (n >> 24);
1848
1849 wzSource += 5;
1850 pbDest += 4;
1851 cchSource -= 5;
1852 }
1853
1854 if (cchSource)
1855 {
1856 n = 0;
1857 for (i = 0; i < cchSource; ++i)
1858 {
1859 k = Base85DecodeTable[wzSource[i]];
1860 if (85 == k)
1861 {
1862 // illegal symbol
1863 return E_UNEXPECTED;
1864 }
1865
1866 n += k * Base85PowerTable[i];
1867 }
1868
1869 for (i = 1; i < cchSource; ++i)
1870 {
1871 *pbDest++ = (BYTE)n;
1872 n >>= 8;
1873 }
1874
1875 if (0 != n)
1876 {
1877 // decode error
1878 return E_UNEXPECTED;
1879 }
1880 }
1881
1882 hr = S_OK;
1883
1884LExit:
1885 return hr;
1886}
1887
1888
1889/****************************************************************************
1890MultiSzLen - calculates the length of a MULTISZ string including all nulls
1891including the double null terminator at the end of the MULTISZ.
1892
1893NOTE: returns 0 if the multisz in not properly terminated with two nulls
1894****************************************************************************/
1895extern "C" HRESULT DAPI MultiSzLen(
1896 __in_ecount(*pcch) __nullnullterminated LPCWSTR pwzMultiSz,
1897 __out SIZE_T* pcch
1898)
1899{
1900 Assert(pcch);
1901
1902 HRESULT hr = S_OK;
1903 LPCWSTR wz = pwzMultiSz;
1904 DWORD_PTR dwMaxSize = 0;
1905
1906 hr = StrMaxLength(pwzMultiSz, &dwMaxSize);
1907 StrExitOnFailure(hr, "failed to get the max size of a string while calculating MULTISZ length");
1908
1909 *pcch = 0;
1910 while (*pcch < dwMaxSize)
1911 {
1912 if (L'\0' == *wz && L'\0' == *(wz + 1))
1913 {
1914 break;
1915 }
1916
1917 ++wz;
1918 *pcch = *pcch + 1;
1919 }
1920
1921 // Add two for the last 2 NULLs (that we looked ahead at)
1922 *pcch = *pcch + 2;
1923
1924 // If we've walked off the end then the length is 0
1925 if (*pcch > dwMaxSize)
1926 {
1927 *pcch = 0;
1928 }
1929
1930LExit:
1931 return hr;
1932}
1933
1934
1935/****************************************************************************
1936MultiSzPrepend - prepends a string onto the front of a MUTLISZ
1937
1938****************************************************************************/
1939extern "C" HRESULT DAPI MultiSzPrepend(
1940 __deref_inout_ecount(*pcchMultiSz) __nullnullterminated LPWSTR* ppwzMultiSz,
1941 __inout_opt SIZE_T* pcchMultiSz,
1942 __in __nullnullterminated LPCWSTR pwzInsert
1943 )
1944{
1945 Assert(ppwzMultiSz && pwzInsert && *pwzInsert);
1946
1947 HRESULT hr =S_OK;
1948 LPWSTR pwzResult = NULL;
1949 SIZE_T cchResult = 0;
1950 SIZE_T cchInsert = 0;
1951 SIZE_T cchMultiSz = 0;
1952
1953 // Get the lengths of the MULTISZ (and prime it if it's not initialized)
1954 if (pcchMultiSz && 0 != *pcchMultiSz)
1955 {
1956 cchMultiSz = *pcchMultiSz;
1957 }
1958 else
1959 {
1960 hr = MultiSzLen(*ppwzMultiSz, &cchMultiSz);
1961 StrExitOnFailure(hr, "failed to get length of multisz");
1962 }
1963
1964 hr = ::StringCchLengthW(pwzInsert, STRSAFE_MAX_CCH, reinterpret_cast<size_t*>(&cchInsert));
1965 StrExitOnRootFailure(hr, "failed to get length of insert string");
1966
1967 cchResult = cchInsert + cchMultiSz + 1;
1968
1969 // Allocate the result buffer
1970 hr = StrAlloc(&pwzResult, cchResult + 1);
1971 StrExitOnFailure(hr, "failed to allocate result string");
1972
1973 // Prepend
1974 hr = ::StringCchCopyW(pwzResult, cchResult, pwzInsert);
1975 StrExitOnRootFailure(hr, "failed to copy prepend string: %ls", pwzInsert);
1976
1977 // If there was no MULTISZ, double null terminate our result, otherwise, copy the MULTISZ in
1978 if (0 == cchMultiSz)
1979 {
1980 pwzResult[cchResult] = L'\0';
1981 ++cchResult;
1982 }
1983 else
1984 {
1985 // Copy the rest
1986 ::CopyMemory(pwzResult + cchInsert + 1, *ppwzMultiSz, cchMultiSz * sizeof(WCHAR));
1987
1988 // Free the old buffer
1989 ReleaseNullStr(*ppwzMultiSz);
1990 }
1991
1992 // Set the result
1993 *ppwzMultiSz = pwzResult;
1994
1995 if (pcchMultiSz)
1996 {
1997 *pcchMultiSz = cchResult;
1998 }
1999
2000 pwzResult = NULL;
2001
2002LExit:
2003 ReleaseNullStr(pwzResult);
2004
2005 return hr;
2006}
2007
2008/****************************************************************************
2009MultiSzFindSubstring - case insensitive find of a string in a MULTISZ that contains the
2010specified sub string and returns the index of the
2011string in the MULTISZ, the address, neither, or both
2012
2013NOTE: returns S_FALSE if the string is not found
2014****************************************************************************/
2015extern "C" HRESULT DAPI MultiSzFindSubstring(
2016 __in __nullnullterminated LPCWSTR pwzMultiSz,
2017 __in __nullnullterminated LPCWSTR pwzSubstring,
2018 __out_opt DWORD_PTR* pdwIndex,
2019 __deref_opt_out __nullnullterminated LPCWSTR* ppwzFoundIn
2020 )
2021{
2022 Assert(pwzMultiSz && *pwzMultiSz && pwzSubstring && *pwzSubstring);
2023
2024 HRESULT hr = S_FALSE; // Assume we won't find it (the glass is half empty)
2025 LPCWSTR wz = pwzMultiSz;
2026 DWORD_PTR dwIndex = 0;
2027 SIZE_T cchMultiSz = 0;
2028 SIZE_T cchProgress = 0;
2029
2030 hr = MultiSzLen(pwzMultiSz, &cchMultiSz);
2031 StrExitOnFailure(hr, "failed to get the length of a MULTISZ string");
2032
2033 // Find the string containing the sub string
2034 hr = S_OK;
2035 while (NULL == wcsistr(wz, pwzSubstring))
2036 {
2037 // Slide through to the end of the current string
2038 while (L'\0' != *wz && cchProgress < cchMultiSz)
2039 {
2040 ++wz;
2041 ++cchProgress;
2042 }
2043
2044 // If we're done, we're done
2045 if (L'\0' == *(wz + 1) || cchProgress >= cchMultiSz)
2046 {
2047 hr = S_FALSE;
2048 break;
2049 }
2050
2051 // Move on to the next string
2052 ++wz;
2053 ++dwIndex;
2054 }
2055 Assert(S_OK == hr || S_FALSE == hr);
2056
2057 // If we found it give them what they want
2058 if (S_OK == hr)
2059 {
2060 if (pdwIndex)
2061 {
2062 *pdwIndex = dwIndex;
2063 }
2064
2065 if (ppwzFoundIn)
2066 {
2067 *ppwzFoundIn = wz;
2068 }
2069 }
2070
2071LExit:
2072 return hr;
2073}
2074
2075/****************************************************************************
2076MultiSzFindString - finds a string in a MULTISZ and returns the index of
2077the string in the MULTISZ, the address or both
2078
2079NOTE: returns S_FALSE if the string is not found
2080****************************************************************************/
2081extern "C" HRESULT DAPI MultiSzFindString(
2082 __in __nullnullterminated LPCWSTR pwzMultiSz,
2083 __in __nullnullterminated LPCWSTR pwzString,
2084 __out_opt DWORD_PTR* pdwIndex,
2085 __deref_opt_out __nullnullterminated LPCWSTR* ppwzFound
2086 )
2087{
2088 Assert(pwzMultiSz && *pwzMultiSz && pwzString && *pwzString && (pdwIndex || ppwzFound));
2089
2090 HRESULT hr = S_FALSE; // Assume we won't find it
2091 LPCWSTR wz = pwzMultiSz;
2092 DWORD_PTR dwIndex = 0;
2093 SIZE_T cchMutliSz = 0;
2094 SIZE_T cchProgress = 0;
2095
2096 hr = MultiSzLen(pwzMultiSz, &cchMutliSz);
2097 StrExitOnFailure(hr, "failed to get the length of a MULTISZ string");
2098
2099 // Find the string
2100 hr = S_OK;
2101 while (0 != lstrcmpW(wz, pwzString))
2102 {
2103 // Slide through to the end of the current string
2104 while (L'\0' != *wz && cchProgress < cchMutliSz)
2105 {
2106 ++wz;
2107 ++cchProgress;
2108 }
2109
2110 // If we're done, we're done
2111 if (L'\0' == *(wz + 1) || cchProgress >= cchMutliSz)
2112 {
2113 hr = S_FALSE;
2114 break;
2115 }
2116
2117 // Move on to the next string
2118 ++wz;
2119 ++dwIndex;
2120 }
2121 Assert(S_OK == hr || S_FALSE == hr);
2122
2123 // If we found it give them what they want
2124 if (S_OK == hr)
2125 {
2126 if (pdwIndex)
2127 {
2128 *pdwIndex = dwIndex;
2129 }
2130
2131 if (ppwzFound)
2132 {
2133 *ppwzFound = wz;
2134 }
2135 }
2136
2137LExit:
2138 return hr;
2139}
2140
2141/****************************************************************************
2142MultiSzRemoveString - removes string from a MULTISZ at the specified
2143index
2144
2145NOTE: does an in place removal without shrinking the memory allocation
2146
2147NOTE: returns S_FALSE if the MULTISZ has fewer strings than dwIndex
2148****************************************************************************/
2149extern "C" HRESULT DAPI MultiSzRemoveString(
2150 __deref_inout __nullnullterminated LPWSTR* ppwzMultiSz,
2151 __in DWORD_PTR dwIndex
2152 )
2153{
2154 Assert(ppwzMultiSz && *ppwzMultiSz);
2155
2156 HRESULT hr = S_OK;
2157 LPCWSTR wz = *ppwzMultiSz;
2158 LPCWSTR wzNext = NULL;
2159 DWORD_PTR dwCurrentIndex = 0;
2160 SIZE_T cchMultiSz = 0;
2161 SIZE_T cchProgress = 0;
2162
2163 hr = MultiSzLen(*ppwzMultiSz, &cchMultiSz);
2164 StrExitOnFailure(hr, "failed to get the length of a MULTISZ string");
2165
2166 // Find the index we want to remove
2167 hr = S_OK;
2168 while (dwCurrentIndex < dwIndex)
2169 {
2170 // Slide through to the end of the current string
2171 while (L'\0' != *wz && cchProgress < cchMultiSz)
2172 {
2173 ++wz;
2174 ++cchProgress;
2175 }
2176
2177 // If we're done, we're done
2178 if (L'\0' == *(wz + 1) || cchProgress >= cchMultiSz)
2179 {
2180 hr = S_FALSE;
2181 break;
2182 }
2183
2184 // Move on to the next string
2185 ++wz;
2186 ++cchProgress;
2187 ++dwCurrentIndex;
2188 }
2189 Assert(S_OK == hr || S_FALSE == hr);
2190
2191 // If we found the index to be removed
2192 if (S_OK == hr)
2193 {
2194 wzNext = wz;
2195
2196 // Slide through to the end of the current string
2197 while (L'\0' != *wzNext && cchProgress < cchMultiSz)
2198 {
2199 ++wzNext;
2200 ++cchProgress;
2201 }
2202
2203 // Something weird has happened if we're past the end of the MULTISZ
2204 if (cchProgress > cchMultiSz)
2205 {
2206 hr = E_UNEXPECTED;
2207 StrExitOnFailure(hr, "failed to move past the string to be removed from MULTISZ");
2208 }
2209
2210 // Move on to the next character
2211 ++wzNext;
2212 ++cchProgress;
2213
2214 ::MoveMemory((LPVOID)wz, (LPVOID)wzNext, (cchMultiSz - cchProgress) * sizeof(WCHAR));
2215 }
2216
2217LExit:
2218 return hr;
2219}
2220
2221/****************************************************************************
2222MultiSzInsertString - inserts new string at the specified index
2223
2224****************************************************************************/
2225extern "C" HRESULT DAPI MultiSzInsertString(
2226 __deref_inout __nullnullterminated LPWSTR* ppwzMultiSz,
2227 __inout_opt SIZE_T* pcchMultiSz,
2228 __in DWORD_PTR dwIndex,
2229 __in_z LPCWSTR pwzInsert
2230 )
2231{
2232 Assert(ppwzMultiSz && pwzInsert && *pwzInsert);
2233
2234 HRESULT hr = S_OK;
2235 LPCWSTR wz = *ppwzMultiSz;
2236 DWORD_PTR dwCurrentIndex = 0;
2237 SIZE_T cchProgress = 0;
2238 LPWSTR pwzResult = NULL;
2239 SIZE_T cchResult = 0;
2240 SIZE_T cchString = 0;
2241 SIZE_T cchMultiSz = 0;
2242
2243 hr = ::StringCchLengthW(pwzInsert, STRSAFE_MAX_CCH, reinterpret_cast<size_t*>(&cchString));
2244 StrExitOnRootFailure(hr, "failed to get length of insert string");
2245
2246 if (pcchMultiSz && 0 != *pcchMultiSz)
2247 {
2248 cchMultiSz = *pcchMultiSz;
2249 }
2250 else
2251 {
2252 hr = MultiSzLen(*ppwzMultiSz, &cchMultiSz);
2253 StrExitOnFailure(hr, "failed to get the length of a MULTISZ string");
2254 }
2255
2256 // Find the index we want to insert at
2257 hr = S_OK;
2258 while (dwCurrentIndex < dwIndex)
2259 {
2260 // Slide through to the end of the current string
2261 while (L'\0' != *wz && cchProgress < cchMultiSz)
2262 {
2263 ++wz;
2264 ++cchProgress;
2265 }
2266
2267 // If we're done, we're done
2268 if ((dwCurrentIndex + 1 != dwIndex && L'\0' == *(wz + 1)) || cchProgress >= cchMultiSz)
2269 {
2270 hr = HRESULT_FROM_WIN32(ERROR_OBJECT_NOT_FOUND);
2271 StrExitOnRootFailure(hr, "requested to insert into an invalid index: %u in a MULTISZ", dwIndex);
2272 }
2273
2274 // Move on to the next string
2275 ++wz;
2276 ++cchProgress;
2277 ++dwCurrentIndex;
2278 }
2279
2280 //
2281 // Insert the string
2282 //
2283 cchResult = cchMultiSz + cchString + 1;
2284
2285 hr = StrAlloc(&pwzResult, cchResult);
2286 StrExitOnFailure(hr, "failed to allocate result string for MULTISZ insert");
2287
2288 // Copy the part before the insert
2289 ::CopyMemory(pwzResult, *ppwzMultiSz, cchProgress * sizeof(WCHAR));
2290
2291 // Copy the insert part
2292 ::CopyMemory(pwzResult + cchProgress, pwzInsert, (cchString + 1) * sizeof(WCHAR));
2293
2294 // Copy the part after the insert
2295 ::CopyMemory(pwzResult + cchProgress + cchString + 1, wz, (cchMultiSz - cchProgress) * sizeof(WCHAR));
2296
2297 // Free the old buffer
2298 ReleaseNullStr(*ppwzMultiSz);
2299
2300 // Set the result
2301 *ppwzMultiSz = pwzResult;
2302
2303 // If they wanted the resulting length, let 'em have it
2304 if (pcchMultiSz)
2305 {
2306 *pcchMultiSz = cchResult;
2307 }
2308
2309 pwzResult = NULL;
2310
2311LExit:
2312 ReleaseStr(pwzResult);
2313
2314 return hr;
2315}
2316
2317/****************************************************************************
2318MultiSzReplaceString - replaces string at the specified index with a new one
2319
2320****************************************************************************/
2321extern "C" HRESULT DAPI MultiSzReplaceString(
2322 __deref_inout __nullnullterminated LPWSTR* ppwzMultiSz,
2323 __in DWORD_PTR dwIndex,
2324 __in_z LPCWSTR pwzString
2325 )
2326{
2327 Assert(ppwzMultiSz && pwzString && *pwzString);
2328
2329 HRESULT hr = S_OK;
2330
2331 hr = MultiSzRemoveString(ppwzMultiSz, dwIndex);
2332 StrExitOnFailure(hr, "failed to remove string from MULTISZ at the specified index: %u", dwIndex);
2333
2334 hr = MultiSzInsertString(ppwzMultiSz, NULL, dwIndex, pwzString);
2335 StrExitOnFailure(hr, "failed to insert string into MULTISZ at the specified index: %u", dwIndex);
2336
2337LExit:
2338 return hr;
2339}
2340
2341
2342/****************************************************************************
2343wcsistr - case insensitive find a substring
2344
2345****************************************************************************/
2346extern "C" LPCWSTR DAPI wcsistr(
2347 __in_z LPCWSTR wzString,
2348 __in_z LPCWSTR wzCharSet
2349 )
2350{
2351 LPCWSTR wzSource = wzString;
2352 LPCWSTR wzSearch = NULL;
2353 SIZE_T cchSourceIndex = 0;
2354
2355 // Walk through wzString (the source string) one character at a time
2356 while (*wzSource)
2357 {
2358 cchSourceIndex = 0;
2359 wzSearch = wzCharSet;
2360
2361 // Look ahead in the source string until we get a full match or we hit the end of the source
2362 while (L'\0' != wzSource[cchSourceIndex] && L'\0' != *wzSearch && towlower(wzSource[cchSourceIndex]) == towlower(*wzSearch))
2363 {
2364 ++cchSourceIndex;
2365 ++wzSearch;
2366 }
2367
2368 // If we found it, return the point that we found it at
2369 if (L'\0' == *wzSearch)
2370 {
2371 return wzSource;
2372 }
2373
2374 // Walk ahead one character
2375 ++wzSource;
2376 }
2377
2378 return NULL;
2379}
2380
2381/****************************************************************************
2382StrStringToInt16 - converts a string to a signed 16-bit integer.
2383
2384****************************************************************************/
2385extern "C" HRESULT DAPI StrStringToInt16(
2386 __in_z LPCWSTR wzIn,
2387 __in DWORD cchIn,
2388 __out SHORT* psOut
2389 )
2390{
2391 HRESULT hr = S_OK;
2392 LONGLONG ll = 0;
2393
2394 hr = StrStringToInt64(wzIn, cchIn, &ll);
2395 StrExitOnFailure(hr, "Failed to parse int64.");
2396
2397 if (SHORT_MAX < ll || SHORT_MIN > ll)
2398 {
2399 ExitFunction1(hr = DISP_E_OVERFLOW);
2400 }
2401 *psOut = (SHORT)ll;
2402
2403LExit:
2404 return hr;
2405}
2406
2407/****************************************************************************
2408StrStringToUInt16 - converts a string to an unsigned 16-bit integer.
2409
2410****************************************************************************/
2411extern "C" HRESULT DAPI StrStringToUInt16(
2412 __in_z LPCWSTR wzIn,
2413 __in DWORD cchIn,
2414 __out USHORT* pusOut
2415 )
2416{
2417 HRESULT hr = S_OK;
2418 ULONGLONG ull = 0;
2419
2420 hr = StrStringToUInt64(wzIn, cchIn, &ull);
2421 StrExitOnFailure(hr, "Failed to parse uint64.");
2422
2423 if (USHORT_MAX < ull)
2424 {
2425 ExitFunction1(hr = DISP_E_OVERFLOW);
2426 }
2427 *pusOut = (USHORT)ull;
2428
2429LExit:
2430 return hr;
2431}
2432
2433/****************************************************************************
2434StrStringToInt32 - converts a string to a signed 32-bit integer.
2435
2436****************************************************************************/
2437extern "C" HRESULT DAPI StrStringToInt32(
2438 __in_z LPCWSTR wzIn,
2439 __in DWORD cchIn,
2440 __out INT* piOut
2441 )
2442{
2443 HRESULT hr = S_OK;
2444 LONGLONG ll = 0;
2445
2446 hr = StrStringToInt64(wzIn, cchIn, &ll);
2447 StrExitOnFailure(hr, "Failed to parse int64.");
2448
2449 if (INT_MAX < ll || INT_MIN > ll)
2450 {
2451 ExitFunction1(hr = DISP_E_OVERFLOW);
2452 }
2453 *piOut = (INT)ll;
2454
2455LExit:
2456 return hr;
2457}
2458
2459/****************************************************************************
2460StrStringToUInt32 - converts a string to an unsigned 32-bit integer.
2461
2462****************************************************************************/
2463extern "C" HRESULT DAPI StrStringToUInt32(
2464 __in_z LPCWSTR wzIn,
2465 __in DWORD cchIn,
2466 __out UINT* puiOut
2467 )
2468{
2469 HRESULT hr = S_OK;
2470 ULONGLONG ull = 0;
2471
2472 hr = StrStringToUInt64(wzIn, cchIn, &ull);
2473 StrExitOnFailure(hr, "Failed to parse uint64.");
2474
2475 if (UINT_MAX < ull)
2476 {
2477 ExitFunction1(hr = DISP_E_OVERFLOW);
2478 }
2479 *puiOut = (UINT)ull;
2480
2481LExit:
2482 return hr;
2483}
2484
2485/****************************************************************************
2486StrStringToInt64 - converts a string to a signed 64-bit integer.
2487
2488****************************************************************************/
2489extern "C" HRESULT DAPI StrStringToInt64(
2490 __in_z LPCWSTR wzIn,
2491 __in DWORD cchIn,
2492 __out LONGLONG* pllOut
2493 )
2494{
2495 HRESULT hr = S_OK;
2496 DWORD i = 0;
2497 INT iSign = 1;
2498 INT nDigit = 0;
2499 LARGE_INTEGER liValue = { };
2500 size_t cchString = 0;
2501
2502 // get string length if not provided
2503 if (0 >= cchIn)
2504 {
2505 hr = ::StringCchLengthW(wzIn, STRSAFE_MAX_CCH, &cchString);
2506 StrExitOnRootFailure(hr, "Failed to get length of string.");
2507
2508 cchIn = (DWORD)cchString;
2509 if (0 >= cchIn)
2510 {
2511 ExitFunction1(hr = E_INVALIDARG);
2512 }
2513 }
2514
2515 // check sign
2516 if (L'-' == wzIn[0])
2517 {
2518 if (1 >= cchIn)
2519 {
2520 ExitFunction1(hr = E_INVALIDARG);
2521 }
2522 i = 1;
2523 iSign = -1;
2524 }
2525
2526 // read digits
2527 while (i < cchIn)
2528 {
2529 nDigit = wzIn[i] - L'0';
2530 if (0 > nDigit || 9 < nDigit)
2531 {
2532 ExitFunction1(hr = E_INVALIDARG);
2533 }
2534 liValue.QuadPart = liValue.QuadPart * 10 + nDigit * iSign;
2535
2536 if ((liValue.HighPart ^ iSign) & INT_MIN)
2537 {
2538 ExitFunction1(hr = DISP_E_OVERFLOW);
2539 }
2540 ++i;
2541 }
2542
2543 *pllOut = liValue.QuadPart;
2544
2545LExit:
2546 return hr;
2547}
2548
2549/****************************************************************************
2550StrStringToUInt64 - converts a string to an unsigned 64-bit integer.
2551
2552****************************************************************************/
2553extern "C" HRESULT DAPI StrStringToUInt64(
2554 __in_z LPCWSTR wzIn,
2555 __in DWORD cchIn,
2556 __out ULONGLONG* pullOut
2557 )
2558{
2559 HRESULT hr = S_OK;
2560 DWORD i = 0;
2561 DWORD nDigit = 0;
2562 ULONGLONG ullValue = 0;
2563 ULONGLONG ull = 0;
2564 size_t cchString = 0;
2565
2566 // get string length if not provided
2567 if (0 >= cchIn)
2568 {
2569 hr = ::StringCchLengthW(wzIn, STRSAFE_MAX_CCH, &cchString);
2570 StrExitOnRootFailure(hr, "Failed to get length of string.");
2571
2572 cchIn = (DWORD)cchString;
2573 if (0 >= cchIn)
2574 {
2575 ExitFunction1(hr = E_INVALIDARG);
2576 }
2577 }
2578
2579 // read digits
2580 while (i < cchIn)
2581 {
2582 nDigit = wzIn[i] - L'0';
2583 if (9 < nDigit)
2584 {
2585 ExitFunction1(hr = E_INVALIDARG);
2586 }
2587 ull = (ULONGLONG)(ullValue * 10 + nDigit);
2588
2589 if (ull < ullValue)
2590 {
2591 ExitFunction1(hr = DISP_E_OVERFLOW);
2592 }
2593 ullValue = ull;
2594 ++i;
2595 }
2596
2597 *pullOut = ullValue;
2598
2599LExit:
2600 return hr;
2601}
2602
2603/****************************************************************************
2604StrStringToUpper - alters the given string in-place to be entirely uppercase
2605
2606****************************************************************************/
2607void DAPI StrStringToUpper(
2608 __inout_z LPWSTR wzIn
2609 )
2610{
2611 ::CharUpperBuffW(wzIn, lstrlenW(wzIn));
2612}
2613
2614/****************************************************************************
2615StrStringToLower - alters the given string in-place to be entirely lowercase
2616
2617****************************************************************************/
2618void DAPI StrStringToLower(
2619 __inout_z LPWSTR wzIn
2620 )
2621{
2622 ::CharLowerBuffW(wzIn, lstrlenW(wzIn));
2623}
2624
2625/****************************************************************************
2626StrAllocStringToUpperInvariant - creates an upper-case copy of a string.
2627
2628****************************************************************************/
2629extern "C" HRESULT DAPI StrAllocStringToUpperInvariant(
2630 __deref_out_z LPWSTR* pscz,
2631 __in_z LPCWSTR wzSource,
2632 __in SIZE_T cchSource
2633 )
2634{
2635 return StrAllocStringMapInvariant(pscz, wzSource, cchSource, LCMAP_UPPERCASE);
2636}
2637
2638/****************************************************************************
2639StrAllocStringToLowerInvariant - creates an lower-case copy of a string.
2640
2641****************************************************************************/
2642extern "C" HRESULT DAPI StrAllocStringToLowerInvariant(
2643 __deref_out_z LPWSTR* pscz,
2644 __in_z LPCWSTR wzSource,
2645 __in SIZE_T cchSource
2646 )
2647{
2648 return StrAllocStringMapInvariant(pscz, wzSource, cchSource, LCMAP_LOWERCASE);
2649}
2650
2651/****************************************************************************
2652StrArrayAllocString - Allocates a string array.
2653
2654****************************************************************************/
2655extern "C" HRESULT DAPI StrArrayAllocString(
2656 __deref_inout_ecount_opt(*pcStrArray) LPWSTR **prgsczStrArray,
2657 __inout LPUINT pcStrArray,
2658 __in_z LPCWSTR wzSource,
2659 __in SIZE_T cchSource
2660 )
2661{
2662 HRESULT hr = S_OK;
2663 UINT cNewStrArray;
2664
2665 hr = ::UIntAdd(*pcStrArray, 1, &cNewStrArray);
2666 StrExitOnFailure(hr, "Failed to increment the string array element count.");
2667
2668 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(prgsczStrArray), cNewStrArray, sizeof(LPWSTR), ARRAY_GROWTH_SIZE);
2669 StrExitOnFailure(hr, "Failed to allocate memory for the string array.");
2670
2671 hr = StrAllocString(&(*prgsczStrArray)[*pcStrArray], wzSource, cchSource);
2672 StrExitOnFailure(hr, "Failed to allocate and assign the string.");
2673
2674 *pcStrArray = cNewStrArray;
2675
2676LExit:
2677 return hr;
2678}
2679
2680/****************************************************************************
2681StrArrayFree - Frees a string array.
2682
2683Use ReleaseNullStrArray to nullify the arguments.
2684
2685****************************************************************************/
2686extern "C" HRESULT DAPI StrArrayFree(
2687 __in_ecount(cStrArray) LPWSTR *rgsczStrArray,
2688 __in UINT cStrArray
2689 )
2690{
2691 HRESULT hr = S_OK;
2692
2693 for (UINT i = 0; i < cStrArray; ++i)
2694 {
2695 if (NULL != rgsczStrArray[i])
2696 {
2697 hr = StrFree(rgsczStrArray[i]);
2698 StrExitOnFailure(hr, "Failed to free the string at index %u.", i);
2699 }
2700 }
2701
2702 hr = MemFree(rgsczStrArray);
2703 StrExitOnFailure(hr, "Failed to free memory for the string array.");
2704
2705LExit:
2706 return hr;
2707}
2708
2709/****************************************************************************
2710StrSplitAllocArray - Splits a string into an array.
2711
2712****************************************************************************/
2713extern "C" HRESULT DAPI StrSplitAllocArray(
2714 __deref_inout_ecount_opt(*pcStrArray) LPWSTR **prgsczStrArray,
2715 __inout LPUINT pcStrArray,
2716 __in_z LPCWSTR wzSource,
2717 __in_z LPCWSTR wzDelim
2718 )
2719{
2720 HRESULT hr = S_OK;
2721 LPWSTR sczCopy = NULL;
2722 LPWSTR wzContext = NULL;
2723
2724 // Copy wzSource so it is not modified.
2725 hr = StrAllocString(&sczCopy, wzSource, 0);
2726 StrExitOnFailure(hr, "Failed to copy the source string.");
2727
2728 for (LPCWSTR wzToken = ::wcstok_s(sczCopy, wzDelim, &wzContext); wzToken; wzToken = ::wcstok_s(NULL, wzDelim, &wzContext))
2729 {
2730 hr = StrArrayAllocString(prgsczStrArray, pcStrArray, wzToken, 0);
2731 StrExitOnFailure(hr, "Failed to add the string to the string array.");
2732 }
2733
2734LExit:
2735 ReleaseStr(sczCopy);
2736
2737 return hr;
2738}
2739
2740/****************************************************************************
2741StrAllocStringMapInvariant - helper function for the ToUpper and ToLower.
2742
2743Note: Assumes source and destination buffers will be the same.
2744****************************************************************************/
2745static HRESULT StrAllocStringMapInvariant(
2746 __deref_out_z LPWSTR* pscz,
2747 __in_z LPCWSTR wzSource,
2748 __in SIZE_T cchSource,
2749 __in DWORD dwMapFlags
2750 )
2751{
2752 HRESULT hr = S_OK;
2753
2754 hr = StrAllocString(pscz, wzSource, cchSource);
2755 StrExitOnFailure(hr, "Failed to allocate a copy of the source string.");
2756
2757 if (0 == cchSource)
2758 {
2759 // Need the actual string size for LCMapString. This includes the null-terminator
2760 // but LCMapString doesn't care either way.
2761 hr = ::StringCchLengthW(*pscz, INT_MAX, reinterpret_cast<size_t*>(&cchSource));
2762 StrExitOnRootFailure(hr, "Failed to get the length of the string.");
2763 }
2764 else if (INT_MAX < cchSource)
2765 {
2766 StrExitOnRootFailure(hr = E_INVALIDARG, "Source string is too long: %Iu", cchSource);
2767 }
2768
2769 // Convert the copy of the string to upper or lower case in-place.
2770 if (0 == ::LCMapStringW(LOCALE_INVARIANT, dwMapFlags, *pscz, static_cast<int>(cchSource), *pscz, static_cast<int>(cchSource)))
2771 {
2772 StrExitWithLastError(hr, "Failed to convert the string case.");
2773 }
2774
2775LExit:
2776 return hr;
2777}
2778
2779/****************************************************************************
2780StrSecureZeroString - zeroes out string to the make sure the contents
2781don't remain in memory.
2782
2783****************************************************************************/
2784extern "C" DAPI_(HRESULT) StrSecureZeroString(
2785 __in LPWSTR pwz
2786 )
2787{
2788 HRESULT hr = S_OK;
2789 SIZE_T cch;
2790
2791 if (pwz)
2792 {
2793 cch = MemSize(pwz);
2794 if (-1 == cch)
2795 {
2796 hr = E_INVALIDARG;
2797 StrExitOnFailure(hr, "Failed to get size of string");
2798 }
2799 else
2800 {
2801 SecureZeroMemory(pwz, cch);
2802 }
2803 }
2804
2805LExit:
2806 return hr;
2807}
2808
2809/****************************************************************************
2810StrSecureZeroFreeString - zeroes out string to the make sure the contents
2811don't remain in memory, then frees the string.
2812
2813****************************************************************************/
2814extern "C" DAPI_(HRESULT) StrSecureZeroFreeString(
2815 __in LPWSTR pwz
2816 )
2817{
2818 HRESULT hr = S_OK;
2819
2820 hr = StrSecureZeroString(pwz);
2821 ReleaseStr(pwz);
2822
2823 return hr;
2824}
diff --git a/src/libs/dutil/WixToolset.DUtil/svcutil.cpp b/src/libs/dutil/WixToolset.DUtil/svcutil.cpp
new file mode 100644
index 00000000..1a39bfee
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/svcutil.cpp
@@ -0,0 +1,59 @@
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// Exit macros
7#define SvcExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_SVCUTIL, x, s, __VA_ARGS__)
8#define SvcExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_SVCUTIL, x, s, __VA_ARGS__)
9#define SvcExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_SVCUTIL, x, s, __VA_ARGS__)
10#define SvcExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_SVCUTIL, x, s, __VA_ARGS__)
11#define SvcExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_SVCUTIL, x, s, __VA_ARGS__)
12#define SvcExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_SVCUTIL, x, s, __VA_ARGS__)
13#define SvcExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_SVCUTIL, p, x, e, s, __VA_ARGS__)
14#define SvcExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_SVCUTIL, p, x, s, __VA_ARGS__)
15#define SvcExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_SVCUTIL, p, x, e, s, __VA_ARGS__)
16#define SvcExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_SVCUTIL, p, x, s, __VA_ARGS__)
17#define SvcExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_SVCUTIL, e, x, s, __VA_ARGS__)
18#define SvcExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_SVCUTIL, g, x, s, __VA_ARGS__)
19
20/********************************************************************
21SvcQueryConfig - queries the configuration of a service
22
23********************************************************************/
24extern "C" HRESULT DAPI SvcQueryConfig(
25 __in SC_HANDLE sch,
26 __out QUERY_SERVICE_CONFIGW** ppConfig
27 )
28{
29 HRESULT hr = S_OK;
30 QUERY_SERVICE_CONFIGW* pConfig = NULL;
31 DWORD cbConfig = 0;
32
33 if (!::QueryServiceConfigW(sch, NULL, 0, &cbConfig))
34 {
35 DWORD er = ::GetLastError();
36 if (ERROR_INSUFFICIENT_BUFFER == er)
37 {
38 pConfig = static_cast<QUERY_SERVICE_CONFIGW*>(MemAlloc(cbConfig, TRUE));
39 SvcExitOnNull(pConfig, hr, E_OUTOFMEMORY, "Failed to allocate memory to get configuration.");
40
41 if (!::QueryServiceConfigW(sch, pConfig, cbConfig, &cbConfig))
42 {
43 SvcExitWithLastError(hr, "Failed to read service configuration.");
44 }
45 }
46 else
47 {
48 SvcExitOnWin32Error(er, hr, "Failed to query service configuration.");
49 }
50 }
51
52 *ppConfig = pConfig;
53 pConfig = NULL;
54
55LExit:
56 ReleaseMem(pConfig);
57
58 return hr;
59}
diff --git a/src/libs/dutil/WixToolset.DUtil/thmutil.cpp b/src/libs/dutil/WixToolset.DUtil/thmutil.cpp
new file mode 100644
index 00000000..d200a0ea
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/thmutil.cpp
@@ -0,0 +1,5709 @@
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// Exit macros
7#define ThmExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_THMUTIL, x, s, __VA_ARGS__)
8#define ThmExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_THMUTIL, x, s, __VA_ARGS__)
9#define ThmExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_THMUTIL, x, s, __VA_ARGS__)
10#define ThmExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_THMUTIL, x, s, __VA_ARGS__)
11#define ThmExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_THMUTIL, x, s, __VA_ARGS__)
12#define ThmExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_THMUTIL, x, s, __VA_ARGS__)
13#define ThmExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_THMUTIL, p, x, e, s, __VA_ARGS__)
14#define ThmExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_THMUTIL, p, x, s, __VA_ARGS__)
15#define ThmExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_THMUTIL, p, x, e, s, __VA_ARGS__)
16#define ThmExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_THMUTIL, p, x, s, __VA_ARGS__)
17#define ThmExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_THMUTIL, e, x, s, __VA_ARGS__)
18#define ThmExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_THMUTIL, g, x, s, __VA_ARGS__)
19
20// from CommCtrl.h
21#ifndef BS_COMMANDLINK
22#define BS_COMMANDLINK 0x0000000EL
23#endif
24
25#ifndef BCM_SETNOTE
26#define BCM_SETNOTE (BCM_FIRST + 0x0009)
27#endif
28
29#ifndef BCM_SETSHIELD
30#define BCM_SETSHIELD (BCM_FIRST + 0x000C)
31#endif
32
33#ifndef LWS_NOPREFIX
34#define LWS_NOPREFIX 0x0004
35#endif
36
37const DWORD THEME_INVALID_ID = 0xFFFFFFFF;
38const COLORREF THEME_INVISIBLE_COLORREF = 0xFFFFFFFF;
39const DWORD GROW_FONT_INSTANCES = 3;
40const DWORD GROW_WINDOW_TEXT = 250;
41const LPCWSTR THEME_WC_HYPERLINK = L"ThemeHyperLink";
42const LPCWSTR THEME_WC_PANEL = L"ThemePanel";
43const LPCWSTR THEME_WC_STATICOWNERDRAW = L"ThemeStaticOwnerDraw";
44
45static Gdiplus::GdiplusStartupInput vgsi;
46static Gdiplus::GdiplusStartupOutput vgso = { };
47static ULONG_PTR vgdiToken = 0;
48static ULONG_PTR vgdiHookToken = 0;
49static HMODULE vhHyperlinkRegisteredModule = NULL;
50static HMODULE vhPanelRegisteredModule = NULL;
51static HMODULE vhStaticOwnerDrawRegisteredModule = NULL;
52static WNDPROC vpfnStaticOwnerDrawBaseWndProc = NULL;
53static HMODULE vhModuleMsftEdit = NULL;
54static HMODULE vhModuleRichEd = NULL;
55static HCURSOR vhCursorHand = NULL;
56
57enum INTERNAL_CONTROL_STYLE
58{
59 INTERNAL_CONTROL_STYLE_HIDE_WHEN_DISABLED = 0x0001,
60 INTERNAL_CONTROL_STYLE_FILESYSTEM_AUTOCOMPLETE = 0x0002,
61 INTERNAL_CONTROL_STYLE_DISABLED = 0x0004,
62 INTERNAL_CONTROL_STYLE_HIDDEN = 0x0008,
63 INTERNAL_CONTROL_STYLE_OWNER_DRAW = 0x0010,
64};
65
66struct MEMBUFFER_FOR_RICHEDIT
67{
68 BYTE* rgbData;
69 DWORD cbData;
70
71 DWORD iData;
72};
73
74
75// prototypes
76static HRESULT RegisterWindowClasses(
77 __in_opt HMODULE hModule
78 );
79static HRESULT ParseTheme(
80 __in_opt HMODULE hModule,
81 __in_opt LPCWSTR wzRelativePath,
82 __in IXMLDOMDocument* pixd,
83 __out THEME** ppTheme
84 );
85static HRESULT ParseImage(
86 __in_opt HMODULE hModule,
87 __in_z_opt LPCWSTR wzRelativePath,
88 __in IXMLDOMNode* pElement,
89 __out HBITMAP* phImage
90 );
91static HRESULT ParseIcon(
92 __in_opt HMODULE hModule,
93 __in_z_opt LPCWSTR wzRelativePath,
94 __in IXMLDOMNode* pElement,
95 __out HICON* phIcon
96 );
97static HRESULT ParseWindow(
98 __in_opt HMODULE hModule,
99 __in_opt LPCWSTR wzRelativePath,
100 __in IXMLDOMElement* pElement,
101 __in THEME* pTheme
102 );
103static HRESULT GetFontColor(
104 __in IXMLDOMNode* pixn,
105 __in_z LPCWSTR wzAttributeName,
106 __out COLORREF* pColorRef,
107 __out DWORD* pdwSystemColor
108 );
109static HRESULT ParseFonts(
110 __in IXMLDOMElement* pElement,
111 __in THEME* pTheme
112 );
113static HRESULT ParsePages(
114 __in_opt HMODULE hModule,
115 __in_opt LPCWSTR wzRelativePath,
116 __in IXMLDOMNode* pElement,
117 __in THEME* pTheme
118 );
119static HRESULT ParseImageLists(
120 __in_opt HMODULE hModule,
121 __in_opt LPCWSTR wzRelativePath,
122 __in IXMLDOMNode* pElement,
123 __in THEME* pTheme
124 );
125static HRESULT ParseControls(
126 __in_opt HMODULE hModule,
127 __in_opt LPCWSTR wzRelativePath,
128 __in IXMLDOMNode* pElement,
129 __in THEME* pTheme,
130 __in_opt THEME_CONTROL* pParentControl,
131 __in_opt THEME_PAGE* pPage
132 );
133static HRESULT ParseControl(
134 __in_opt HMODULE hModule,
135 __in_opt LPCWSTR wzRelativePath,
136 __in IXMLDOMNode* pixn,
137 __in THEME* pTheme,
138 __in THEME_CONTROL* pControl,
139 __in BOOL fSkipDimensions,
140 __in_opt THEME_PAGE* pPage
141 );
142static HRESULT ParseActions(
143 __in IXMLDOMNode* pixn,
144 __in THEME_CONTROL* pControl
145 );
146static HRESULT ParseColumns(
147 __in IXMLDOMNode* pixn,
148 __in THEME_CONTROL* pControl
149 );
150static HRESULT ParseRadioButtons(
151 __in_opt HMODULE hModule,
152 __in_opt LPCWSTR wzRelativePath,
153 __in IXMLDOMNode* pixn,
154 __in THEME* pTheme,
155 __in_opt THEME_CONTROL* pParentControl,
156 __in THEME_PAGE* pPage
157 );
158static HRESULT ParseTabs(
159 __in IXMLDOMNode* pixn,
160 __in THEME_CONTROL* pControl
161 );
162static HRESULT ParseText(
163 __in IXMLDOMNode* pixn,
164 __in THEME_CONTROL* pControl,
165 __inout BOOL* pfAnyChildren
166);
167static HRESULT ParseTooltips(
168 __in IXMLDOMNode* pixn,
169 __in THEME_CONTROL* pControl,
170 __inout BOOL* pfAnyChildren
171 );
172static HRESULT ParseNotes(
173 __in IXMLDOMNode* pixn,
174 __in THEME_CONTROL* pControl,
175 __out BOOL* pfAnyChildren
176 );
177static HRESULT StopBillboard(
178 __in THEME* pTheme,
179 __in DWORD dwControl
180 );
181static HRESULT StartBillboard(
182 __in THEME* pTheme,
183 __in DWORD dwControl
184 );
185static HRESULT EnsureFontInstance(
186 __in THEME* pTheme,
187 __in THEME_FONT* pFont,
188 __out THEME_FONT_INSTANCE** ppFontInstance
189 );
190static HRESULT FindImageList(
191 __in THEME* pTheme,
192 __in_z LPCWSTR wzImageListName,
193 __out HIMAGELIST *phImageList
194 );
195static HRESULT LoadControls(
196 __in THEME* pTheme,
197 __in_opt THEME_CONTROL* pParentControl,
198 __in_ecount_opt(cAssignControlIds) const THEME_ASSIGN_CONTROL_ID* rgAssignControlIds,
199 __in DWORD cAssignControlIds
200 );
201static HRESULT ShowControl(
202 __in THEME* pTheme,
203 __in THEME_CONTROL* pControl,
204 __in int nCmdShow,
205 __in BOOL fSaveEditboxes,
206 __in THEME_SHOW_PAGE_REASON reason,
207 __in DWORD dwPageId,
208 __out_opt HWND* phwndFocus
209 );
210static HRESULT ShowControls(
211 __in THEME* pTheme,
212 __in_opt const THEME_CONTROL* pParentControl,
213 __in int nCmdShow,
214 __in BOOL fSaveEditboxes,
215 __in THEME_SHOW_PAGE_REASON reason,
216 __in DWORD dwPageId
217 );
218static HRESULT DrawButton(
219 __in THEME* pTheme,
220 __in DRAWITEMSTRUCT* pdis,
221 __in const THEME_CONTROL* pControl
222 );
223static void DrawControlText(
224 __in THEME* pTheme,
225 __in DRAWITEMSTRUCT* pdis,
226 __in const THEME_CONTROL* pControl,
227 __in BOOL fCentered,
228 __in BOOL fDrawFocusRect
229 );
230static HRESULT DrawHyperlink(
231 __in THEME* pTheme,
232 __in DRAWITEMSTRUCT* pdis,
233 __in const THEME_CONTROL* pControl
234 );
235static HRESULT DrawImage(
236 __in THEME* pTheme,
237 __in DRAWITEMSTRUCT* pdis,
238 __in const THEME_CONTROL* pControl
239 );
240static HRESULT DrawProgressBar(
241 __in THEME* pTheme,
242 __in DRAWITEMSTRUCT* pdis,
243 __in const THEME_CONTROL* pControl
244 );
245static BOOL DrawHoverControl(
246 __in THEME* pTheme,
247 __in BOOL fHover
248 );
249static DWORD CALLBACK RichEditStreamFromFileHandleCallback(
250 __in DWORD_PTR dwCookie,
251 __in_bcount(cb) LPBYTE pbBuff,
252 __in LONG cb,
253 __in LONG *pcb
254 );
255static DWORD CALLBACK RichEditStreamFromMemoryCallback(
256 __in DWORD_PTR dwCookie,
257 __in_bcount(cb) LPBYTE pbBuff,
258 __in LONG cb,
259 __in LONG *pcb
260 );
261static void FreeFontInstance(
262 __in THEME_FONT_INSTANCE* pFontInstance
263 );
264static void FreeFont(
265 __in THEME_FONT* pFont
266 );
267static void FreePage(
268 __in THEME_PAGE* pPage
269 );
270static void FreeControl(
271 __in THEME_CONTROL* pControl
272 );
273static void FreeConditionalText(
274 __in THEME_CONDITIONAL_TEXT* pConditionalText
275 );
276static void FreeImageList(
277 __in THEME_IMAGELIST* pImageList
278 );
279static void FreeAction(
280 __in THEME_ACTION* pAction
281 );
282static void FreeColumn(
283 __in THEME_COLUMN* pColumn
284 );
285static void FreeTab(
286 __in THEME_TAB* pTab
287 );
288static void CALLBACK OnBillboardTimer(
289 __in THEME* pTheme,
290 __in HWND hwnd,
291 __in UINT_PTR idEvent
292 );
293static void OnBrowseDirectory(
294 __in THEME* pTheme,
295 __in HWND hWnd,
296 __in const THEME_ACTION* pAction
297 );
298static BOOL OnButtonClicked(
299 __in THEME* pTheme,
300 __in HWND hWnd,
301 __in const THEME_CONTROL* pControl
302 );
303static BOOL OnDpiChanged(
304 __in THEME* pTheme,
305 __in WPARAM wParam,
306 __in LPARAM lParam
307 );
308static void OnNcCreate(
309 __in THEME* pTheme,
310 __in HWND hWnd,
311 __in LPARAM lParam
312 );
313static HRESULT OnRichEditEnLink(
314 __in LPARAM lParam,
315 __in HWND hWndRichEdit,
316 __in HWND hWnd
317 );
318static BOOL ControlIsType(
319 __in const THEME* pTheme,
320 __in DWORD dwControl,
321 __in THEME_CONTROL_TYPE type
322 );
323static const THEME_CONTROL* FindControlFromHWnd(
324 __in const THEME* pTheme,
325 __in HWND hWnd,
326 __in_opt const THEME_CONTROL* pParentControl = NULL
327 );
328static void GetControlDimensions(
329 __in const THEME_CONTROL* pControl,
330 __in const RECT* prcParent,
331 __out int* piWidth,
332 __out int* piHeight,
333 __out int* piX,
334 __out int* piY
335 );
336// Using iWidth as total width of listview, base width of columns, and "Expands" flag on columns
337// calculates final width of each column (storing result in each column's nWidth value)
338static HRESULT SizeListViewColumns(
339 __inout THEME_CONTROL* pControl
340 );
341static LRESULT CALLBACK ControlGroupDefWindowProc(
342 __in_opt THEME* pTheme,
343 __in HWND hWnd,
344 __in UINT uMsg,
345 __in WPARAM wParam,
346 __in LPARAM lParam
347 );
348static LRESULT CALLBACK PanelWndProc(
349 __in HWND hWnd,
350 __in UINT uMsg,
351 __in WPARAM wParam,
352 __in LPARAM lParam
353 );
354static LRESULT CALLBACK StaticOwnerDrawWndProc(
355 __in HWND hWnd,
356 __in UINT uMsg,
357 __in WPARAM wParam,
358 __in LPARAM lParam
359 );
360static HRESULT LocalizeControls(
361 __in DWORD cControls,
362 __in THEME_CONTROL* rgControls,
363 __in const WIX_LOCALIZATION *pWixLoc
364 );
365static HRESULT LocalizeControl(
366 __in THEME_CONTROL* pControl,
367 __in const WIX_LOCALIZATION *pWixLoc
368 );
369static HRESULT LoadControlsString(
370 __in DWORD cControls,
371 __in THEME_CONTROL* rgControls,
372 __in HMODULE hResModule
373 );
374static HRESULT LoadControlString(
375 __in THEME_CONTROL* pControl,
376 __in HMODULE hResModule
377 );
378static void ResizeControls(
379 __in DWORD cControls,
380 __in THEME_CONTROL* rgControls,
381 __in const RECT* prcParent
382 );
383static void ResizeControl(
384 __in THEME_CONTROL* pControl,
385 __in const RECT* prcParent
386 );
387static void ScaleThemeFromWindow(
388 __in THEME* pTheme,
389 __in UINT nDpi,
390 __in int x,
391 __in int y
392 );
393static void ScaleTheme(
394 __in THEME* pTheme,
395 __in UINT nDpi,
396 __in int x,
397 __in int y,
398 __in DWORD dwStyle,
399 __in BOOL fMenu,
400 __in DWORD dwExStyle
401 );
402static void ScaleControls(
403 __in THEME* pTheme,
404 __in DWORD cControls,
405 __in THEME_CONTROL* rgControls,
406 __in UINT nDpi
407 );
408static void ScaleControl(
409 __in THEME* pTheme,
410 __in THEME_CONTROL* pControl,
411 __in UINT nDpi
412 );
413static void GetControls(
414 __in THEME* pTheme,
415 __in_opt THEME_CONTROL* pParentControl,
416 __out DWORD** ppcControls,
417 __out THEME_CONTROL*** pprgControls
418 );
419static void GetControls(
420 __in const THEME* pTheme,
421 __in_opt const THEME_CONTROL* pParentControl,
422 __out DWORD& cControls,
423 __out THEME_CONTROL*& rgControls
424 );
425static void UnloadControls(
426 __in DWORD cControls,
427 __in THEME_CONTROL* rgControls
428 );
429
430
431// Public functions.
432
433DAPI_(HRESULT) ThemeInitialize(
434 __in_opt HMODULE hModule
435 )
436{
437 HRESULT hr = S_OK;
438 INITCOMMONCONTROLSEX icex = { };
439
440 DpiuInitialize();
441
442 hr = XmlInitialize();
443 ThmExitOnFailure(hr, "Failed to initialize XML.");
444
445 hr = RegisterWindowClasses(hModule);
446 ThmExitOnFailure(hr, "Failed to register theme window classes.");
447
448 // Initialize GDI+ and common controls.
449 vgsi.SuppressBackgroundThread = TRUE;
450
451 hr = GdipInitialize(&vgsi, &vgdiToken, &vgso);
452 ThmExitOnFailure(hr, "Failed to initialize GDI+.");
453
454 icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
455 icex.dwICC = ICC_STANDARD_CLASSES | ICC_PROGRESS_CLASS | ICC_LISTVIEW_CLASSES | ICC_TREEVIEW_CLASSES | ICC_TAB_CLASSES | ICC_LINK_CLASS;
456 ::InitCommonControlsEx(&icex);
457
458 (*vgso.NotificationHook)(&vgdiHookToken);
459
460LExit:
461 return hr;
462}
463
464
465DAPI_(void) ThemeUninitialize()
466{
467 if (vhModuleMsftEdit)
468 {
469 ::FreeLibrary(vhModuleMsftEdit);
470 vhModuleMsftEdit = NULL;
471 }
472
473 if (vhModuleRichEd)
474 {
475 ::FreeLibrary(vhModuleRichEd);
476 vhModuleRichEd = NULL;
477 }
478
479 if (vhHyperlinkRegisteredModule)
480 {
481 ::UnregisterClassW(THEME_WC_HYPERLINK, vhHyperlinkRegisteredModule);
482 vhHyperlinkRegisteredModule = NULL;
483 }
484
485 if (vhPanelRegisteredModule)
486 {
487 ::UnregisterClassW(THEME_WC_PANEL, vhPanelRegisteredModule);
488 vhPanelRegisteredModule = NULL;
489 }
490
491 if (vhStaticOwnerDrawRegisteredModule)
492 {
493 ::UnregisterClassW(THEME_WC_STATICOWNERDRAW, vhStaticOwnerDrawRegisteredModule);
494 vhStaticOwnerDrawRegisteredModule = NULL;
495 vpfnStaticOwnerDrawBaseWndProc = NULL;
496 }
497
498 if (vgdiToken)
499 {
500 GdipUninitialize(vgdiToken);
501 vgdiToken = 0;
502 }
503
504 XmlUninitialize();
505 DpiuUninitialize();
506}
507
508
509DAPI_(HRESULT) ThemeLoadFromFile(
510 __in_z LPCWSTR wzThemeFile,
511 __out THEME** ppTheme
512 )
513{
514 HRESULT hr = S_OK;
515 IXMLDOMDocument* pixd = NULL;
516 LPWSTR sczRelativePath = NULL;
517
518 hr = XmlLoadDocumentFromFile(wzThemeFile, &pixd);
519 ThmExitOnFailure(hr, "Failed to load theme resource as XML document.");
520
521 hr = PathGetDirectory(wzThemeFile, &sczRelativePath);
522 ThmExitOnFailure(hr, "Failed to get relative path from theme file.");
523
524 hr = ParseTheme(NULL, sczRelativePath, pixd, ppTheme);
525 ThmExitOnFailure(hr, "Failed to parse theme.");
526
527LExit:
528 ReleaseStr(sczRelativePath);
529 ReleaseObject(pixd);
530
531 return hr;
532}
533
534
535DAPI_(HRESULT) ThemeLoadFromResource(
536 __in_opt HMODULE hModule,
537 __in_z LPCSTR szResource,
538 __out THEME** ppTheme
539 )
540{
541 HRESULT hr = S_OK;
542 LPVOID pvResource = NULL;
543 DWORD cbResource = 0;
544 LPWSTR sczXml = NULL;
545 IXMLDOMDocument* pixd = NULL;
546
547 hr = ResReadData(hModule, szResource, &pvResource, &cbResource);
548 ThmExitOnFailure(hr, "Failed to read theme from resource.");
549
550 hr = StrAllocStringAnsi(&sczXml, reinterpret_cast<LPCSTR>(pvResource), cbResource, CP_UTF8);
551 ThmExitOnFailure(hr, "Failed to convert XML document data from UTF-8 to unicode string.");
552
553 hr = XmlLoadDocument(sczXml, &pixd);
554 ThmExitOnFailure(hr, "Failed to load theme resource as XML document.");
555
556 hr = ParseTheme(hModule, NULL, pixd, ppTheme);
557 ThmExitOnFailure(hr, "Failed to parse theme.");
558
559LExit:
560 ReleaseObject(pixd);
561 ReleaseStr(sczXml);
562
563 return hr;
564}
565
566
567DAPI_(void) ThemeFree(
568 __in THEME* pTheme
569 )
570{
571 if (pTheme)
572 {
573 for (DWORD i = 0; i < pTheme->cFonts; ++i)
574 {
575 FreeFont(pTheme->rgFonts + i);
576 }
577
578 for (DWORD i = 0; i < pTheme->cPages; ++i)
579 {
580 FreePage(pTheme->rgPages + i);
581 }
582
583 for (DWORD i = 0; i < pTheme->cImageLists; ++i)
584 {
585 FreeImageList(pTheme->rgImageLists + i);
586 }
587
588 for (DWORD i = 0; i < pTheme->cControls; ++i)
589 {
590 FreeControl(pTheme->rgControls + i);
591 }
592
593 ReleaseMem(pTheme->rgControls);
594 ReleaseMem(pTheme->rgPages);
595 ReleaseMem(pTheme->rgFonts);
596
597 if (pTheme->hImage)
598 {
599 ::DeleteBitmap(pTheme->hImage);
600 }
601
602 ReleaseStr(pTheme->sczCaption);
603 ReleaseMem(pTheme);
604 }
605}
606
607DAPI_(HRESULT) ThemeRegisterVariableCallbacks(
608 __in THEME* pTheme,
609 __in_opt PFNTHM_EVALUATE_VARIABLE_CONDITION pfnEvaluateCondition,
610 __in_opt PFNTHM_FORMAT_VARIABLE_STRING pfnFormatString,
611 __in_opt PFNTHM_GET_VARIABLE_NUMERIC pfnGetNumericVariable,
612 __in_opt PFNTHM_SET_VARIABLE_NUMERIC pfnSetNumericVariable,
613 __in_opt PFNTHM_GET_VARIABLE_STRING pfnGetStringVariable,
614 __in_opt PFNTHM_SET_VARIABLE_STRING pfnSetStringVariable,
615 __in_opt LPVOID pvContext
616 )
617{
618 HRESULT hr = S_OK;
619 ThmExitOnNull(pTheme, hr, S_FALSE, "Theme must be loaded first.");
620
621 pTheme->pfnEvaluateCondition = pfnEvaluateCondition;
622 pTheme->pfnFormatString = pfnFormatString;
623 pTheme->pfnGetNumericVariable = pfnGetNumericVariable;
624 pTheme->pfnSetNumericVariable = pfnSetNumericVariable;
625 pTheme->pfnGetStringVariable = pfnGetStringVariable;
626 pTheme->pfnSetStringVariable = pfnSetStringVariable;
627 pTheme->pvVariableContext = pvContext;
628
629LExit:
630 return hr;
631}
632
633
634DAPI_(HRESULT) ThemeCreateParentWindow(
635 __in THEME* pTheme,
636 __in DWORD dwExStyle,
637 __in LPCWSTR szClassName,
638 __in LPCWSTR szWindowName,
639 __in DWORD dwStyle,
640 __in int x,
641 __in int y,
642 __in_opt HWND hwndParent,
643 __in_opt HINSTANCE hInstance,
644 __in_opt LPVOID lpParam,
645 __in THEME_WINDOW_INITIAL_POSITION initialPosition,
646 __out_opt HWND* phWnd
647 )
648{
649 HRESULT hr = S_OK;
650 DPIU_MONITOR_CONTEXT* pMonitorContext = NULL;
651 POINT pt = { };
652 RECT* pMonitorRect = NULL;
653 HMENU hMenu = NULL;
654 HWND hWnd = NULL;
655
656 if (pTheme->hwndParent)
657 {
658 ThmExitOnFailure(hr = E_INVALIDSTATE, "ThemeCreateParentWindow called after the theme was loaded.");
659 }
660
661 if (THEME_WINDOW_INITIAL_POSITION_CENTER_MONITOR_FROM_COORDINATES == initialPosition)
662 {
663 pt.x = x;
664 pt.y = y;
665 hr = DpiuGetMonitorContextFromPoint(&pt, &pMonitorContext);
666 if (SUCCEEDED(hr))
667 {
668 pMonitorRect = &pMonitorContext->mi.rcWork;
669 if (pMonitorContext->nDpi != pTheme->nDpi)
670 {
671 ScaleTheme(pTheme, pMonitorContext->nDpi, pMonitorRect->left, pMonitorRect->top, dwStyle, NULL != hMenu, dwExStyle);
672 }
673
674 x = pMonitorRect->left + (pMonitorRect->right - pMonitorRect->left - pTheme->nWindowWidth) / 2;
675 y = pMonitorRect->top + (pMonitorRect->bottom - pMonitorRect->top - pTheme->nWindowHeight) / 2;
676 }
677 else
678 {
679 hr = S_OK;
680 x = CW_USEDEFAULT;
681 y = CW_USEDEFAULT;
682 }
683 }
684
685 hWnd = ::CreateWindowExW(dwExStyle, szClassName, szWindowName, dwStyle, x, y, pTheme->nWindowWidth, pTheme->nWindowHeight, hwndParent, hMenu, hInstance, lpParam);
686 ThmExitOnNullWithLastError(hWnd, hr, "Failed to create theme parent window.");
687 ThmExitOnNull(pTheme->hwndParent, hr, E_INVALIDSTATE, "Theme parent window is not set, make sure ThemeDefWindowProc is called for WM_NCCREATE.");
688 AssertSz(hWnd == pTheme->hwndParent, "Theme parent window does not equal newly created window.");
689
690 if (phWnd)
691 {
692 *phWnd = hWnd;
693 }
694
695LExit:
696 ReleaseMem(pMonitorContext);
697
698 return hr;
699}
700
701
702DAPI_(HRESULT) ThemeLoadControls(
703 __in THEME* pTheme,
704 __in_ecount_opt(cAssignControlIds) const THEME_ASSIGN_CONTROL_ID* rgAssignControlIds,
705 __in DWORD cAssignControlIds
706 )
707{
708 HRESULT hr = S_OK;
709
710 if (!pTheme->hwndParent)
711 {
712 ThmExitOnFailure(hr = E_INVALIDSTATE, "ThemeLoadControls called before theme parent window created.");
713 }
714
715 hr = LoadControls(pTheme, NULL, rgAssignControlIds, cAssignControlIds);
716
717LExit:
718 return hr;
719}
720
721
722DAPI_(void) ThemeUnloadControls(
723 __in THEME* pTheme
724 )
725{
726 UnloadControls(pTheme->cControls, pTheme->rgControls);
727
728 pTheme->hwndHover = NULL;
729 pTheme->hwndParent = NULL;
730}
731
732DAPI_(HRESULT) ThemeLocalize(
733 __in THEME *pTheme,
734 __in const WIX_LOCALIZATION *pWixLoc
735 )
736{
737 HRESULT hr = S_OK;
738 LPWSTR sczCaption = NULL;
739
740 hr = LocLocalizeString(pWixLoc, &pTheme->sczCaption);
741 ThmExitOnFailure(hr, "Failed to localize theme caption.");
742
743 if (pTheme->pfnFormatString)
744 {
745 hr = pTheme->pfnFormatString(pTheme->sczCaption, &sczCaption, pTheme->pvVariableContext);
746 if (SUCCEEDED(hr))
747 {
748 hr = ThemeUpdateCaption(pTheme, sczCaption);
749 }
750 }
751
752 hr = LocalizeControls(pTheme->cControls, pTheme->rgControls, pWixLoc);
753
754LExit:
755 ReleaseStr(sczCaption);
756
757 return hr;
758}
759
760/********************************************************************
761 ThemeLoadStrings - Loads string resources.
762 Must be called after loading a theme and before calling
763 ThemeLoadControls.
764*******************************************************************/
765DAPI_(HRESULT) ThemeLoadStrings(
766 __in THEME* pTheme,
767 __in HMODULE hResModule
768 )
769{
770 HRESULT hr = S_OK;
771 ThmExitOnNull(pTheme, hr, S_FALSE, "Theme must be loaded first.");
772
773 if (UINT_MAX != pTheme->uStringId)
774 {
775 hr = ResReadString(hResModule, pTheme->uStringId, &pTheme->sczCaption);
776 ThmExitOnFailure(hr, "Failed to load theme caption.");
777 }
778
779 hr = LoadControlsString(pTheme->cControls, pTheme->rgControls, hResModule);
780
781LExit:
782 return hr;
783}
784
785
786DAPI_(HRESULT) ThemeLoadRichEditFromFile(
787 __in THEME* pTheme,
788 __in DWORD dwControl,
789 __in_z LPCWSTR wzFileName,
790 __in HMODULE hModule
791 )
792{
793 HRESULT hr = S_OK;
794 LPWSTR sczFile = NULL;
795 HANDLE hFile = INVALID_HANDLE_VALUE;
796 HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl);
797
798 hr = PathRelativeToModule(&sczFile, wzFileName, hModule);
799 ThmExitOnFailure(hr, "Failed to read resource data.");
800
801 hFile = ::CreateFileW(sczFile, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
802 if (INVALID_HANDLE_VALUE == hFile)
803 {
804 ThmExitWithLastError(hr, "Failed to open RTF file.");
805 }
806 else
807 {
808 LONGLONG llRtfSize;
809 hr = FileSizeByHandle(hFile, &llRtfSize);
810 if (SUCCEEDED(hr))
811 {
812 ::SendMessageW(hWnd, EM_EXLIMITTEXT, 0, static_cast<LPARAM>(llRtfSize));
813 }
814
815 EDITSTREAM es = { };
816 es.pfnCallback = RichEditStreamFromFileHandleCallback;
817 es.dwCookie = reinterpret_cast<DWORD_PTR>(hFile);
818
819 ::SendMessageW(hWnd, EM_STREAMIN, SF_RTF, reinterpret_cast<LPARAM>(&es));
820 hr = es.dwError;
821 ThmExitOnFailure(hr, "Failed to update RTF stream.");
822 }
823
824LExit:
825 ReleaseStr(sczFile);
826 ReleaseFile(hFile);
827
828 return hr;
829}
830
831
832DAPI_(HRESULT) ThemeLoadRichEditFromResource(
833 __in THEME* pTheme,
834 __in DWORD dwControl,
835 __in_z LPCSTR szResourceName,
836 __in HMODULE hModule
837 )
838{
839 HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl);
840 return ThemeLoadRichEditFromResourceToHWnd(hWnd, szResourceName, hModule);
841}
842
843DAPI_(HRESULT) ThemeLoadRichEditFromResourceToHWnd(
844 __in HWND hWnd,
845 __in_z LPCSTR szResourceName,
846 __in HMODULE hModule
847 )
848{
849 HRESULT hr = S_OK;
850 MEMBUFFER_FOR_RICHEDIT buffer = { };
851 EDITSTREAM es = { };
852
853 hr = ResReadData(hModule, szResourceName, reinterpret_cast<LPVOID*>(&buffer.rgbData), &buffer.cbData);
854 ThmExitOnFailure(hr, "Failed to read resource data.");
855
856 es.pfnCallback = RichEditStreamFromMemoryCallback;
857 es.dwCookie = reinterpret_cast<DWORD_PTR>(&buffer);
858
859 ::SendMessageW(hWnd, EM_STREAMIN, SF_RTF, reinterpret_cast<LPARAM>(&es));
860 hr = es.dwError;
861 ThmExitOnFailure(hr, "Failed to update RTF stream.");
862
863LExit:
864 return hr;
865}
866
867
868DAPI_(BOOL) ThemeHandleKeyboardMessage(
869 __in_opt THEME* pTheme,
870 __in HWND /*hWnd*/,
871 __in MSG* pMsg
872 )
873{
874 return pTheme ? ::IsDialogMessageW(pTheme->hwndParent, pMsg) : FALSE;
875}
876
877
878extern "C" LRESULT CALLBACK ThemeDefWindowProc(
879 __in_opt THEME* pTheme,
880 __in HWND hWnd,
881 __in UINT uMsg,
882 __in WPARAM wParam,
883 __in LPARAM lParam
884 )
885{
886 RECT rcParent = { };
887 RECT *pRect = NULL;
888
889 if (pTheme)
890 {
891 switch (uMsg)
892 {
893 case WM_NCCREATE:
894 if (pTheme->hwndParent)
895 {
896 AssertSz(FALSE, "WM_NCCREATE called multiple times");
897 }
898 else
899 {
900 OnNcCreate(pTheme, hWnd, lParam);
901 }
902 break;
903
904 case WM_NCHITTEST:
905 if (pTheme->dwStyle & WS_POPUP)
906 {
907 return HTCAPTION; // allow pop-up windows to be moved by grabbing any non-control.
908 }
909 break;
910
911 case WM_DPICHANGED:
912 if (OnDpiChanged(pTheme, wParam, lParam))
913 {
914 return 0;
915 }
916 break;
917
918 case WM_SIZING:
919 if (pTheme->fAutoResize)
920 {
921 pRect = reinterpret_cast<RECT *>(lParam);
922 if (pRect->right - pRect->left < pTheme->nMinimumWidth)
923 {
924 if (wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_LEFT || wParam == WMSZ_TOPLEFT)
925 {
926 pRect->left = pRect->right - pTheme->nMinimumWidth;
927 }
928 else
929 {
930 pRect->right = pRect->left + pTheme->nMinimumWidth;
931 }
932 }
933 if (pRect->bottom - pRect->top < pTheme->nMinimumHeight)
934 {
935 if (wParam == WMSZ_BOTTOM || wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_BOTTOMRIGHT)
936 {
937 pRect->bottom = pRect->top + pTheme->nMinimumHeight;
938 }
939 else
940 {
941 pRect->top = pRect->bottom - pTheme->nMinimumHeight;
942 }
943 }
944
945 return TRUE;
946 }
947 break;
948
949 case WM_SIZE:
950 if (pTheme->fAutoResize || pTheme->fForceResize)
951 {
952 pTheme->fForceResize = FALSE;
953 ::GetClientRect(pTheme->hwndParent, &rcParent);
954 ResizeControls(pTheme->cControls, pTheme->rgControls, &rcParent);
955 return 0;
956 }
957 break;
958 }
959 }
960
961 return ControlGroupDefWindowProc(pTheme, hWnd, uMsg, wParam, lParam);
962}
963
964
965DAPI_(void) ThemeGetPageIds(
966 __in const THEME* pTheme,
967 __in_ecount(cGetPages) LPCWSTR* rgwzFindNames,
968 __inout_ecount(cGetPages) DWORD* rgdwPageIds,
969 __in DWORD cGetPages
970 )
971{
972 for (DWORD i = 0; i < cGetPages; ++i)
973 {
974 LPCWSTR wzFindName = rgwzFindNames[i];
975 for (DWORD j = 0; j < pTheme->cPages; ++j)
976 {
977 LPCWSTR wzPageName = pTheme->rgPages[j].sczName;
978 if (wzPageName && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzPageName, -1, wzFindName, -1))
979 {
980 rgdwPageIds[i] = j + 1; // add one to make the page ids 1-based (so zero is invalid).
981 break;
982 }
983 }
984 }
985}
986
987
988DAPI_(THEME_PAGE*) ThemeGetPage(
989 __in const THEME* pTheme,
990 __in DWORD dwPage
991 )
992{
993 DWORD iPage = dwPage - 1;
994 THEME_PAGE* pPage = NULL;
995
996 if (iPage < pTheme->cPages)
997 {
998 pPage = pTheme->rgPages + iPage;
999 }
1000
1001 return pPage;
1002}
1003
1004
1005DAPI_(HRESULT) ThemeShowPage(
1006 __in THEME* pTheme,
1007 __in DWORD dwPage,
1008 __in int nCmdShow
1009 )
1010{
1011 return ThemeShowPageEx(pTheme, dwPage, nCmdShow, THEME_SHOW_PAGE_REASON_DEFAULT);
1012}
1013
1014
1015DAPI_(HRESULT) ThemeShowPageEx(
1016 __in THEME* pTheme,
1017 __in DWORD dwPage,
1018 __in int nCmdShow,
1019 __in THEME_SHOW_PAGE_REASON reason
1020 )
1021{
1022 HRESULT hr = S_OK;
1023 BOOL fHide = SW_HIDE == nCmdShow;
1024 BOOL fSaveEditboxes = FALSE;
1025 THEME_SAVEDVARIABLE* pSavedVariable = NULL;
1026 THEME_PAGE* pPage = ThemeGetPage(pTheme, dwPage);
1027
1028 if (pPage)
1029 {
1030 if (fHide)
1031 {
1032 switch (reason)
1033 {
1034 case THEME_SHOW_PAGE_REASON_DEFAULT:
1035 // Set the variables in the loop below.
1036 fSaveEditboxes = TRUE;
1037 break;
1038 case THEME_SHOW_PAGE_REASON_CANCEL:
1039 if (pPage->cSavedVariables && pTheme->pfnSetStringVariable)
1040 {
1041 // Best effort to cancel any changes to the variables.
1042 for (DWORD v = 0; v < pPage->cSavedVariables; ++v)
1043 {
1044 pSavedVariable = pPage->rgSavedVariables + v;
1045 if (pSavedVariable->wzName)
1046 {
1047 pTheme->pfnSetStringVariable(pSavedVariable->wzName, pSavedVariable->sczValue, FALSE, pTheme->pvVariableContext);
1048 }
1049 }
1050 }
1051 break;
1052 }
1053
1054 if (THEME_SHOW_PAGE_REASON_REFRESH != reason)
1055 {
1056 pPage->cSavedVariables = 0;
1057 if (pPage->rgSavedVariables)
1058 {
1059 SecureZeroMemory(pPage->rgSavedVariables, MemSize(pPage->rgSavedVariables));
1060 }
1061 }
1062
1063 pTheme->dwCurrentPageId = 0;
1064 }
1065 else
1066 {
1067 if (THEME_SHOW_PAGE_REASON_REFRESH == reason)
1068 {
1069 fSaveEditboxes = TRUE;
1070 }
1071 else
1072 {
1073 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pPage->rgSavedVariables), pPage->cControlIndices, sizeof(THEME_SAVEDVARIABLE), pPage->cControlIndices);
1074 ThmExitOnFailure(hr, "Failed to allocate memory for saved variables.");
1075
1076 SecureZeroMemory(pPage->rgSavedVariables, MemSize(pPage->rgSavedVariables));
1077 pPage->cSavedVariables = pPage->cControlIndices;
1078
1079 // Save the variables in the loop below.
1080 }
1081
1082 pTheme->dwCurrentPageId = dwPage;
1083 }
1084 }
1085
1086 hr = ShowControls(pTheme, NULL, nCmdShow, fSaveEditboxes, reason, dwPage);
1087 ThmExitOnFailure(hr, "Failed to show page controls.");
1088
1089LExit:
1090 return hr;
1091}
1092
1093
1094DAPI_(BOOL) ThemeControlExists(
1095 __in const THEME* pTheme,
1096 __in DWORD dwControl
1097 )
1098{
1099 BOOL fExists = FALSE;
1100 HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl);
1101 if (hWnd)
1102 {
1103 const THEME_CONTROL* pControl = FindControlFromHWnd(pTheme, hWnd);
1104 fExists = (pControl && hWnd == pControl->hWnd);
1105 }
1106
1107 return fExists;
1108}
1109
1110
1111DAPI_(void) ThemeControlEnable(
1112 __in THEME* pTheme,
1113 __in DWORD dwControl,
1114 __in BOOL fEnable
1115 )
1116{
1117 HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl);
1118 THEME_CONTROL* pControl = const_cast<THEME_CONTROL*>(FindControlFromHWnd(pTheme, hWnd));
1119 if (pControl)
1120 {
1121 pControl->dwInternalStyle = fEnable ? (pControl->dwInternalStyle & ~INTERNAL_CONTROL_STYLE_DISABLED) : (pControl->dwInternalStyle | INTERNAL_CONTROL_STYLE_DISABLED);
1122 ::EnableWindow(hWnd, fEnable);
1123
1124 if (pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_HIDE_WHEN_DISABLED)
1125 {
1126 ::ShowWindow(hWnd, fEnable ? SW_SHOW : SW_HIDE);
1127 }
1128 }
1129}
1130
1131
1132DAPI_(BOOL) ThemeControlEnabled(
1133 __in THEME* pTheme,
1134 __in DWORD dwControl
1135 )
1136{
1137 HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl);
1138 const THEME_CONTROL* pControl = FindControlFromHWnd(pTheme, hWnd);
1139 return pControl && !(pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_DISABLED);
1140}
1141
1142
1143DAPI_(void) ThemeControlElevates(
1144 __in THEME* pTheme,
1145 __in DWORD dwControl,
1146 __in BOOL fElevates
1147 )
1148{
1149 HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl);
1150 ::SendMessageW(hWnd, BCM_SETSHIELD, 0, fElevates);
1151}
1152
1153
1154DAPI_(void) ThemeShowControl(
1155 __in THEME* pTheme,
1156 __in DWORD dwControl,
1157 __in int nCmdShow
1158 )
1159{
1160 HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl);
1161 ::ShowWindow(hWnd, nCmdShow);
1162
1163 // Save the control's visible state.
1164 THEME_CONTROL* pControl = const_cast<THEME_CONTROL*>(FindControlFromHWnd(pTheme, hWnd));
1165 if (pControl)
1166 {
1167 pControl->dwInternalStyle = (SW_HIDE == nCmdShow) ? (pControl->dwInternalStyle | INTERNAL_CONTROL_STYLE_HIDDEN) : (pControl->dwInternalStyle & ~INTERNAL_CONTROL_STYLE_HIDDEN);
1168 }
1169}
1170
1171
1172DAPI_(void) ThemeShowControlEx(
1173 __in THEME* pTheme,
1174 __in DWORD dwControl,
1175 __in int nCmdShow
1176 )
1177{
1178 HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl);
1179 THEME_CONTROL* pControl = const_cast<THEME_CONTROL*>(FindControlFromHWnd(pTheme, hWnd));
1180 if (pControl)
1181 {
1182 ShowControl(pTheme, pControl, nCmdShow, THEME_CONTROL_TYPE_EDITBOX == pControl->type, THEME_SHOW_PAGE_REASON_REFRESH, 0, NULL);
1183 }
1184}
1185
1186
1187DAPI_(BOOL) ThemeControlVisible(
1188 __in THEME* pTheme,
1189 __in DWORD dwControl
1190 )
1191{
1192 HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl);
1193 return ::IsWindowVisible(hWnd);
1194}
1195
1196
1197DAPI_(BOOL) ThemePostControlMessage(
1198 __in THEME* pTheme,
1199 __in DWORD dwControl,
1200 __in UINT Msg,
1201 __in WPARAM wParam,
1202 __in LPARAM lParam
1203 )
1204{
1205 HRESULT hr = S_OK;
1206 UINT er = ERROR_SUCCESS;
1207 HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl);
1208
1209 if (!::PostMessageW(hWnd, Msg, wParam, lParam))
1210 {
1211 er = ::GetLastError();
1212 hr = HRESULT_FROM_WIN32(er);
1213 }
1214
1215 return SUCCEEDED(hr);
1216}
1217
1218
1219DAPI_(LRESULT) ThemeSendControlMessage(
1220 __in const THEME* pTheme,
1221 __in DWORD dwControl,
1222 __in UINT Msg,
1223 __in WPARAM wParam,
1224 __in LPARAM lParam
1225 )
1226{
1227 HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl);
1228 return ::SendMessageW(hWnd, Msg, wParam, lParam);
1229}
1230
1231
1232DAPI_(HRESULT) ThemeDrawBackground(
1233 __in THEME* pTheme,
1234 __in PAINTSTRUCT* pps
1235 )
1236{
1237 HRESULT hr = S_FALSE;
1238
1239 if (pTheme->hImage && 0 <= pTheme->nSourceX && 0 <= pTheme->nSourceY && pps->fErase)
1240 {
1241 HDC hdcMem = ::CreateCompatibleDC(pps->hdc);
1242 HBITMAP hDefaultBitmap = static_cast<HBITMAP>(::SelectObject(hdcMem, pTheme->hImage));
1243 DWORD dwSourceWidth = pTheme->nDefaultDpiWidth;
1244 DWORD dwSourceHeight = pTheme->nDefaultDpiHeight;
1245
1246 ::StretchBlt(pps->hdc, 0, 0, pTheme->nWidth, pTheme->nHeight, hdcMem, pTheme->nSourceX, pTheme->nSourceY, dwSourceWidth, dwSourceHeight, SRCCOPY);
1247
1248 ::SelectObject(hdcMem, hDefaultBitmap);
1249 ::DeleteDC(hdcMem);
1250
1251 hr = S_OK;
1252 }
1253
1254 return hr;
1255}
1256
1257
1258DAPI_(HRESULT) ThemeDrawControl(
1259 __in THEME* pTheme,
1260 __in DRAWITEMSTRUCT* pdis
1261 )
1262{
1263 HRESULT hr = S_OK;
1264 const THEME_CONTROL* pControl = FindControlFromHWnd(pTheme, pdis->hwndItem);
1265
1266 AssertSz(pControl, "Expected control window from owner draw window.");
1267 AssertSz(pControl->hWnd == pdis->hwndItem, "Expected control window to match owner draw window.");
1268 AssertSz(pControl->nWidth < 1 || pControl->nWidth == pdis->rcItem.right - pdis->rcItem.left, "Expected control window width to match owner draw window width.");
1269 AssertSz(pControl->nHeight < 1 || pControl->nHeight == pdis->rcItem.bottom - pdis->rcItem.top, "Expected control window height to match owner draw window height.");
1270
1271 switch (pControl->type)
1272 {
1273 case THEME_CONTROL_TYPE_BUTTON:
1274 hr = DrawButton(pTheme, pdis, pControl);
1275 ThmExitOnFailure(hr, "Failed to draw button.");
1276 break;
1277
1278 case THEME_CONTROL_TYPE_HYPERLINK:
1279 hr = DrawHyperlink(pTheme, pdis, pControl);
1280 ThmExitOnFailure(hr, "Failed to draw hyperlink.");
1281 break;
1282
1283 case THEME_CONTROL_TYPE_IMAGE:
1284 hr = DrawImage(pTheme, pdis, pControl);
1285 ThmExitOnFailure(hr, "Failed to draw image.");
1286 break;
1287
1288 case THEME_CONTROL_TYPE_PROGRESSBAR:
1289 hr = DrawProgressBar(pTheme, pdis, pControl);
1290 ThmExitOnFailure(hr, "Failed to draw progress bar.");
1291 break;
1292
1293 default:
1294 hr = E_UNEXPECTED;
1295 ThmExitOnRootFailure(hr, "Did not specify an owner draw control to draw.");
1296 }
1297
1298LExit:
1299 return hr;
1300}
1301
1302
1303DAPI_(BOOL) ThemeHoverControl(
1304 __in THEME* pTheme,
1305 __in HWND hwndParent,
1306 __in HWND hwndControl
1307 )
1308{
1309 BOOL fHovered = FALSE;
1310 if (hwndControl != pTheme->hwndHover)
1311 {
1312 if (pTheme->hwndHover && pTheme->hwndHover != hwndParent)
1313 {
1314 DrawHoverControl(pTheme, FALSE);
1315 }
1316
1317 pTheme->hwndHover = hwndControl;
1318
1319 if (pTheme->hwndHover && pTheme->hwndHover != hwndParent)
1320 {
1321 fHovered = DrawHoverControl(pTheme, TRUE);
1322 }
1323 }
1324
1325 return fHovered;
1326}
1327
1328
1329DAPI_(BOOL) ThemeIsControlChecked(
1330 __in THEME* pTheme,
1331 __in DWORD dwControl
1332 )
1333{
1334 HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl);
1335 return BST_CHECKED == ::SendMessageW(hWnd, BM_GETCHECK, 0, 0);
1336}
1337
1338
1339DAPI_(BOOL) ThemeSetControlColor(
1340 __in THEME* pTheme,
1341 __in HDC hdc,
1342 __in HWND hWnd,
1343 __out HBRUSH* phBackgroundBrush
1344 )
1345{
1346 THEME_FONT* pFont = NULL;
1347 BOOL fHasBackground = FALSE;
1348
1349 *phBackgroundBrush = NULL;
1350
1351 if (hWnd == pTheme->hwndParent)
1352 {
1353 pFont = (THEME_INVALID_ID == pTheme->dwFontId) ? NULL : pTheme->rgFonts + pTheme->dwFontId;
1354 }
1355 else
1356 {
1357 const THEME_CONTROL* pControl = FindControlFromHWnd(pTheme, hWnd);
1358 pFont = (!pControl || THEME_INVALID_ID == pControl->dwFontId) ? NULL : pTheme->rgFonts + pControl->dwFontId;
1359 }
1360
1361 if (pFont)
1362 {
1363 if (pFont->hForeground)
1364 {
1365 ::SetTextColor(hdc, pFont->crForeground);
1366 }
1367
1368 if (pFont->hBackground)
1369 {
1370 ::SetBkColor(hdc, pFont->crBackground);
1371
1372 *phBackgroundBrush = pFont->hBackground;
1373 fHasBackground = TRUE;
1374 }
1375 else
1376 {
1377 ::SetBkMode(hdc, TRANSPARENT);
1378 *phBackgroundBrush = static_cast<HBRUSH>(::GetStockObject(NULL_BRUSH));
1379 fHasBackground = TRUE;
1380 }
1381 }
1382
1383 return fHasBackground;
1384}
1385
1386
1387DAPI_(HRESULT) ThemeSetProgressControl(
1388 __in THEME* pTheme,
1389 __in DWORD dwControl,
1390 __in DWORD dwProgressPercentage
1391 )
1392{
1393 HRESULT hr = E_NOTFOUND;
1394 HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl);
1395
1396 if (hWnd)
1397 {
1398 THEME_CONTROL* pControl = const_cast<THEME_CONTROL*>(FindControlFromHWnd(pTheme, hWnd));
1399 if (pControl && THEME_CONTROL_TYPE_PROGRESSBAR == pControl->type)
1400 {
1401 DWORD dwCurrentProgress = LOWORD(pControl->dwData);
1402
1403 if (dwCurrentProgress != dwProgressPercentage)
1404 {
1405 DWORD dwColor = HIWORD(pControl->dwData);
1406 pControl->dwData = MAKEDWORD(dwProgressPercentage, dwColor);
1407
1408 if (pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_OWNER_DRAW)
1409 {
1410 if (!::InvalidateRect(hWnd, NULL, FALSE))
1411 {
1412 ThmExitWithLastError(hr, "Failed to invalidate progress bar window.");
1413 }
1414 }
1415 else
1416 {
1417 ::SendMessageW(hWnd, PBM_SETPOS, dwProgressPercentage, 0);
1418 }
1419
1420 hr = S_OK;
1421 }
1422 else
1423 {
1424 hr = S_FALSE;
1425 }
1426 }
1427 }
1428
1429LExit:
1430 return hr;
1431}
1432
1433
1434DAPI_(HRESULT) ThemeSetProgressControlColor(
1435 __in THEME* pTheme,
1436 __in DWORD dwControl,
1437 __in DWORD dwColorIndex
1438 )
1439{
1440 HRESULT hr = S_FALSE;
1441 HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl);
1442 if (hWnd)
1443 {
1444 THEME_CONTROL* pControl = const_cast<THEME_CONTROL*>(FindControlFromHWnd(pTheme, hWnd));
1445
1446 // Only set color on owner draw progress bars.
1447 if (pControl && (pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_OWNER_DRAW))
1448 {
1449 DWORD dwCurrentColor = HIWORD(pControl->dwData);
1450
1451 if (dwCurrentColor != dwColorIndex)
1452 {
1453 DWORD dwCurrentProgress = LOWORD(pControl->dwData);
1454 pControl->dwData = MAKEDWORD(dwCurrentProgress, dwColorIndex);
1455
1456 if (!::InvalidateRect(hWnd, NULL, FALSE))
1457 {
1458 ThmExitWithLastError(hr, "Failed to invalidate progress bar window.");
1459 }
1460
1461 hr = S_OK;
1462 }
1463 }
1464 }
1465
1466LExit:
1467 return hr;
1468}
1469
1470
1471DAPI_(HRESULT) ThemeSetTextControl(
1472 __in const THEME* pTheme,
1473 __in DWORD dwControl,
1474 __in_z_opt LPCWSTR wzText
1475 )
1476{
1477 return ThemeSetTextControlEx(pTheme, dwControl, FALSE, wzText);
1478}
1479
1480
1481DAPI_(HRESULT) ThemeSetTextControlEx(
1482 __in const THEME* pTheme,
1483 __in DWORD dwControl,
1484 __in BOOL fUpdate,
1485 __in_z_opt LPCWSTR wzText
1486 )
1487{
1488 HRESULT hr = S_OK;
1489 HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl);
1490
1491 if (hWnd)
1492 {
1493 if (fUpdate)
1494 {
1495 ::ShowWindow(hWnd, SW_HIDE);
1496 }
1497
1498 if (!::SetWindowTextW(hWnd, wzText))
1499 {
1500 ThmExitWithLastError(hr, "Failed to set control text.");
1501 }
1502
1503 if (fUpdate)
1504 {
1505 ::ShowWindow(hWnd, SW_SHOW);
1506 }
1507 }
1508
1509LExit:
1510 return hr;
1511}
1512
1513
1514DAPI_(HRESULT) ThemeGetTextControl(
1515 __in const THEME* pTheme,
1516 __in DWORD dwControl,
1517 __inout_z LPWSTR* psczText
1518 )
1519{
1520 HRESULT hr = S_OK;
1521 HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl);
1522 SIZE_T cbSize = 0;
1523 DWORD cchText = 0;
1524 DWORD cchTextRead = 0;
1525
1526 // Ensure the string has room for at least one character.
1527 hr = StrMaxLength(*psczText, &cbSize);
1528 ThmExitOnFailure(hr, "Failed to get text buffer length.");
1529
1530 cchText = (DWORD)min(DWORD_MAX, cbSize);
1531
1532 if (!cchText)
1533 {
1534 cchText = GROW_WINDOW_TEXT;
1535
1536 hr = StrAlloc(psczText, cchText);
1537 ThmExitOnFailure(hr, "Failed to grow text buffer.");
1538 }
1539
1540 // Read (and keep growing buffer) until we finally read less than there
1541 // is room in the buffer.
1542 for (;;)
1543 {
1544 cchTextRead = ::GetWindowTextW(hWnd, *psczText, cchText);
1545 if (cchTextRead + 1 < cchText)
1546 {
1547 break;
1548 }
1549 else
1550 {
1551 cchText = cchTextRead + GROW_WINDOW_TEXT;
1552
1553 hr = StrAlloc(psczText, cchText);
1554 ThmExitOnFailure(hr, "Failed to grow text buffer again.");
1555 }
1556 }
1557
1558LExit:
1559 return hr;
1560}
1561
1562
1563DAPI_(HRESULT) ThemeUpdateCaption(
1564 __in THEME* pTheme,
1565 __in_z LPCWSTR wzCaption
1566 )
1567{
1568 HRESULT hr = S_OK;
1569
1570 hr = StrAllocString(&pTheme->sczCaption, wzCaption, 0);
1571 ThmExitOnFailure(hr, "Failed to update theme caption.");
1572
1573LExit:
1574 return hr;
1575}
1576
1577
1578DAPI_(void) ThemeSetFocus(
1579 __in THEME* pTheme,
1580 __in DWORD dwControl
1581 )
1582{
1583 HWND hwndFocus = ::GetDlgItem(pTheme->hwndParent, dwControl);
1584 if (hwndFocus && !ThemeControlEnabled(pTheme, dwControl))
1585 {
1586 hwndFocus = ::GetNextDlgTabItem(pTheme->hwndParent, hwndFocus, FALSE);
1587 }
1588
1589 if (hwndFocus)
1590 {
1591 ::SetFocus(hwndFocus);
1592 }
1593}
1594
1595
1596DAPI_(void) ThemeShowChild(
1597 __in THEME* pTheme,
1598 __in THEME_CONTROL* pParentControl,
1599 __in DWORD dwIndex
1600 )
1601{
1602 // show one child, hide the rest
1603 for (DWORD i = 0; i < pParentControl->cControls; ++i)
1604 {
1605 THEME_CONTROL* pControl = pParentControl->rgControls + i;
1606 ShowControl(pTheme, pControl, dwIndex == i ? SW_SHOW : SW_HIDE, FALSE, THEME_SHOW_PAGE_REASON_DEFAULT, 0, NULL);
1607 }
1608}
1609
1610
1611// Internal functions.
1612
1613static HRESULT RegisterWindowClasses(
1614 __in_opt HMODULE hModule
1615 )
1616{
1617 HRESULT hr = S_OK;
1618 WNDCLASSW wcHyperlink = { };
1619 WNDCLASSW wcPanel = { };
1620 WNDCLASSW wcStaticOwnerDraw = { };
1621 WNDPROC pfnStaticOwnerDrawBaseWndProc = NULL;
1622
1623 vhCursorHand = ::LoadCursorA(NULL, IDC_HAND);
1624
1625 // Base the theme hyperlink class on a button but give it the "hand" icon.
1626 if (!::GetClassInfoW(NULL, WC_BUTTONW, &wcHyperlink))
1627 {
1628 ThmExitWithLastError(hr, "Failed to get button window class.");
1629 }
1630
1631 wcHyperlink.lpszClassName = THEME_WC_HYPERLINK;
1632#pragma prefast(push)
1633#pragma prefast(disable:25068)
1634 wcHyperlink.hCursor = vhCursorHand;
1635#pragma prefast(pop)
1636
1637 if (!::RegisterClassW(&wcHyperlink))
1638 {
1639 ThmExitWithLastError(hr, "Failed to get button window class.");
1640 }
1641 vhHyperlinkRegisteredModule = hModule;
1642
1643 // Panel is its own do-nothing class.
1644 wcPanel.lpfnWndProc = PanelWndProc;
1645 wcPanel.hInstance = hModule;
1646 wcPanel.hCursor = ::LoadCursorW(NULL, (LPCWSTR) IDC_ARROW);
1647 wcPanel.lpszClassName = THEME_WC_PANEL;
1648 if (!::RegisterClassW(&wcPanel))
1649 {
1650 ThmExitWithLastError(hr, "Failed to register window.");
1651 }
1652 vhPanelRegisteredModule = hModule;
1653
1654 if (!::GetClassInfoW(NULL, WC_STATICW, &wcStaticOwnerDraw))
1655 {
1656 ThmExitWithLastError(hr, "Failed to get static window class.");
1657 }
1658
1659 pfnStaticOwnerDrawBaseWndProc = wcStaticOwnerDraw.lpfnWndProc;
1660 wcStaticOwnerDraw.lpfnWndProc = StaticOwnerDrawWndProc;
1661 wcStaticOwnerDraw.hInstance = hModule;
1662 wcStaticOwnerDraw.lpszClassName = THEME_WC_STATICOWNERDRAW;
1663 if (!::RegisterClassW(&wcStaticOwnerDraw))
1664 {
1665 ThmExitWithLastError(hr, "Failed to register OwnerDraw window class.");
1666 }
1667 vhStaticOwnerDrawRegisteredModule = hModule;
1668 vpfnStaticOwnerDrawBaseWndProc = pfnStaticOwnerDrawBaseWndProc;
1669
1670
1671LExit:
1672 return hr;
1673}
1674
1675static HRESULT ParseTheme(
1676 __in_opt HMODULE hModule,
1677 __in_opt LPCWSTR wzRelativePath,
1678 __in IXMLDOMDocument* pixd,
1679 __out THEME** ppTheme
1680 )
1681{
1682 static WORD wThemeId = 0;
1683
1684 HRESULT hr = S_OK;
1685 THEME* pTheme = NULL;
1686 IXMLDOMElement *pThemeElement = NULL;
1687
1688 hr = pixd->get_documentElement(&pThemeElement);
1689 ThmExitOnFailure(hr, "Failed to get theme element.");
1690
1691 pTheme = static_cast<THEME*>(MemAlloc(sizeof(THEME), TRUE));
1692 ThmExitOnNull(pTheme, hr, E_OUTOFMEMORY, "Failed to allocate memory for theme.");
1693
1694 pTheme->wId = ++wThemeId;
1695 pTheme->nDpi = USER_DEFAULT_SCREEN_DPI;
1696
1697 // Parse the optional background resource image.
1698 hr = ParseImage(hModule, wzRelativePath, pThemeElement, &pTheme->hImage);
1699 ThmExitOnFailure(hr, "Failed while parsing theme image.");
1700
1701 // Parse the fonts.
1702 hr = ParseFonts(pThemeElement, pTheme);
1703 ThmExitOnFailure(hr, "Failed to parse theme fonts.");
1704
1705 // Parse the window element.
1706 hr = ParseWindow(hModule, wzRelativePath, pThemeElement, pTheme);
1707 ThmExitOnFailure(hr, "Failed to parse theme window element.");
1708
1709 *ppTheme = pTheme;
1710 pTheme = NULL;
1711
1712LExit:
1713 ReleaseObject(pThemeElement);
1714
1715 if (pTheme)
1716 {
1717 ThemeFree(pTheme);
1718 }
1719
1720 return hr;
1721}
1722
1723static HRESULT ParseImage(
1724 __in_opt HMODULE hModule,
1725 __in_z_opt LPCWSTR wzRelativePath,
1726 __in IXMLDOMNode* pElement,
1727 __out HBITMAP* phImage
1728 )
1729{
1730 HRESULT hr = S_OK;
1731 BSTR bstr = NULL;
1732 LPWSTR sczImageFile = NULL;
1733 int iResourceId = 0;
1734 Gdiplus::Bitmap* pBitmap = NULL;
1735 *phImage = NULL;
1736
1737 hr = XmlGetAttribute(pElement, L"ImageResource", &bstr);
1738 ThmExitOnFailure(hr, "Failed to get image resource attribute.");
1739
1740 if (S_OK == hr)
1741 {
1742 iResourceId = wcstol(bstr, NULL, 10);
1743
1744 hr = GdipBitmapFromResource(hModule, MAKEINTRESOURCE(iResourceId), &pBitmap);
1745 // Don't fail.
1746 }
1747
1748 ReleaseNullBSTR(bstr);
1749
1750 // Parse the optional background image from a given file.
1751 if (!pBitmap)
1752 {
1753 hr = XmlGetAttribute(pElement, L"ImageFile", &bstr);
1754 ThmExitOnFailure(hr, "Failed to get image file attribute.");
1755
1756 if (S_OK == hr)
1757 {
1758 if (wzRelativePath)
1759 {
1760 hr = PathConcat(wzRelativePath, bstr, &sczImageFile);
1761 ThmExitOnFailure(hr, "Failed to combine image file path.");
1762 }
1763 else
1764 {
1765 hr = PathRelativeToModule(&sczImageFile, bstr, hModule);
1766 ThmExitOnFailure(hr, "Failed to get image filename.");
1767 }
1768
1769 hr = GdipBitmapFromFile(sczImageFile, &pBitmap);
1770 // Don't fail.
1771 }
1772 }
1773
1774 // If there is an image, convert it into a bitmap handle.
1775 if (pBitmap)
1776 {
1777 Gdiplus::Color black;
1778 Gdiplus::Status gs = pBitmap->GetHBITMAP(black, phImage);
1779 ThmExitOnGdipFailure(gs, hr, "Failed to convert GDI+ bitmap into HBITMAP.");
1780 }
1781
1782 hr = S_OK;
1783
1784LExit:
1785 if (pBitmap)
1786 {
1787 delete pBitmap;
1788 }
1789
1790 ReleaseStr(sczImageFile);
1791 ReleaseBSTR(bstr);
1792
1793 return hr;
1794}
1795
1796
1797static HRESULT ParseIcon(
1798 __in_opt HMODULE hModule,
1799 __in_z_opt LPCWSTR wzRelativePath,
1800 __in IXMLDOMNode* pElement,
1801 __out HICON* phIcon
1802 )
1803{
1804 HRESULT hr = S_OK;
1805 BSTR bstr = NULL;
1806 LPWSTR sczImageFile = NULL;
1807 int iResourceId = 0;
1808 *phIcon = NULL;
1809
1810 hr = XmlGetAttribute(pElement, L"IconResource", &bstr);
1811 ThmExitOnFailure(hr, "Failed to get icon resource attribute.");
1812
1813 if (S_OK == hr)
1814 {
1815 iResourceId = wcstol(bstr, NULL, 10);
1816
1817 *phIcon = reinterpret_cast<HICON>(::LoadImageW(hModule, MAKEINTRESOURCEW(iResourceId), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE));
1818 ThmExitOnNullWithLastError(*phIcon, hr, "Failed to load icon.");
1819 }
1820 else
1821 {
1822 ReleaseNullBSTR(bstr);
1823
1824 hr = XmlGetAttribute(pElement, L"IconFile", &bstr);
1825 ThmExitOnFailure(hr, "Failed to get icon file attribute.");
1826
1827 if (S_OK == hr)
1828 {
1829 if (wzRelativePath)
1830 {
1831 hr = PathConcat(wzRelativePath, bstr, &sczImageFile);
1832 ThmExitOnFailure(hr, "Failed to combine image file path.");
1833 }
1834 else
1835 {
1836 hr = PathRelativeToModule(&sczImageFile, bstr, hModule);
1837 ThmExitOnFailure(hr, "Failed to get image filename.");
1838 }
1839
1840 *phIcon = reinterpret_cast<HICON>(::LoadImageW(NULL, sczImageFile, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_LOADFROMFILE));
1841 ThmExitOnNullWithLastError(*phIcon, hr, "Failed to load icon: %ls.", sczImageFile);
1842 }
1843 }
1844
1845LExit:
1846 ReleaseStr(sczImageFile);
1847 ReleaseBSTR(bstr);
1848
1849 return hr;
1850}
1851
1852
1853static HRESULT ParseWindow(
1854 __in_opt HMODULE hModule,
1855 __in_opt LPCWSTR wzRelativePath,
1856 __in IXMLDOMElement* pElement,
1857 __in THEME* pTheme
1858 )
1859{
1860 HRESULT hr = S_OK;
1861 IXMLDOMNode* pixn = NULL;
1862 DWORD dwValue = 0;
1863 BSTR bstr = NULL;
1864 LPWSTR sczIconFile = NULL;
1865
1866 hr = XmlSelectSingleNode(pElement, L"Window", &pixn);
1867 if (S_FALSE == hr)
1868 {
1869 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
1870 }
1871 ThmExitOnFailure(hr, "Failed to find window element.");
1872
1873 hr = XmlGetYesNoAttribute(pixn, L"AutoResize", &pTheme->fAutoResize);
1874 if (E_NOTFOUND == hr)
1875 {
1876 hr = S_OK;
1877 }
1878 ThmExitOnFailure(hr, "Failed to get window AutoResize attribute.");
1879
1880 hr = XmlGetAttributeNumber(pixn, L"Width", &dwValue);
1881 if (S_FALSE == hr)
1882 {
1883 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
1884 ThmExitOnRootFailure(hr, "Failed to find window Width attribute.");
1885 }
1886 ThmExitOnFailure(hr, "Failed to get window Width attribute.");
1887
1888 pTheme->nWidth = pTheme->nDefaultDpiWidth = pTheme->nWindowWidth = dwValue;
1889
1890 hr = XmlGetAttributeNumber(pixn, L"Height", &dwValue);
1891 if (S_FALSE == hr)
1892 {
1893 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
1894 ThmExitOnRootFailure(hr, "Failed to find window Height attribute.");
1895 }
1896 ThmExitOnFailure(hr, "Failed to get window Height attribute.");
1897
1898 pTheme->nHeight = pTheme->nDefaultDpiHeight = pTheme->nWindowHeight = dwValue;
1899
1900 hr = XmlGetAttributeNumber(pixn, L"MinimumWidth", &dwValue);
1901 if (S_FALSE == hr)
1902 {
1903 dwValue = 0;
1904 hr = S_OK;
1905 }
1906 ThmExitOnFailure(hr, "Failed to get window MinimumWidth attribute.");
1907
1908 pTheme->nMinimumWidth = pTheme->nDefaultDpiMinimumWidth = dwValue;
1909
1910 hr = XmlGetAttributeNumber(pixn, L"MinimumHeight", &dwValue);
1911 if (S_FALSE == hr)
1912 {
1913 dwValue = 0;
1914 hr = S_OK;
1915 }
1916 ThmExitOnFailure(hr, "Failed to get window MinimumHeight attribute.");
1917
1918 pTheme->nMinimumHeight = pTheme->nDefaultDpiMinimumHeight = dwValue;
1919
1920 hr = XmlGetAttributeNumber(pixn, L"FontId", &pTheme->dwFontId);
1921 if (S_FALSE == hr)
1922 {
1923 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
1924 ThmExitOnRootFailure(hr, "Failed to find window FontId attribute.");
1925 }
1926 ThmExitOnFailure(hr, "Failed to get window FontId attribute.");
1927
1928 // Get the optional window icon from a resource.
1929 hr = XmlGetAttribute(pixn, L"IconResource", &bstr);
1930 ThmExitOnFailure(hr, "Failed to get window IconResource attribute.");
1931
1932 if (S_OK == hr)
1933 {
1934 pTheme->hIcon = ::LoadIconW(hModule, bstr);
1935 ThmExitOnNullWithLastError(pTheme->hIcon, hr, "Failed to load window icon from IconResource.");
1936
1937 ReleaseNullBSTR(bstr);
1938 }
1939
1940 // Get the optional window icon from a file.
1941 hr = XmlGetAttribute(pixn, L"IconFile", &bstr);
1942 ThmExitOnFailure(hr, "Failed to get window IconFile attribute.");
1943
1944 if (S_OK == hr)
1945 {
1946 if (wzRelativePath)
1947 {
1948 hr = PathConcat(wzRelativePath, bstr, &sczIconFile);
1949 ThmExitOnFailure(hr, "Failed to combine icon file path.");
1950 }
1951 else
1952 {
1953 hr = PathRelativeToModule(&sczIconFile, bstr, hModule);
1954 ThmExitOnFailure(hr, "Failed to get icon filename.");
1955 }
1956
1957 pTheme->hIcon = ::LoadImageW(NULL, sczIconFile, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_LOADFROMFILE);
1958 ThmExitOnNullWithLastError(pTheme->hIcon, hr, "Failed to load window icon from IconFile: %ls.", bstr);
1959
1960 ReleaseNullBSTR(bstr);
1961 }
1962
1963 hr = XmlGetAttributeNumber(pixn, L"SourceX", reinterpret_cast<DWORD*>(&pTheme->nSourceX));
1964 if (S_FALSE == hr)
1965 {
1966 pTheme->nSourceX = -1;
1967 }
1968 ThmExitOnFailure(hr, "Failed to get window SourceX attribute.");
1969
1970 hr = XmlGetAttributeNumber(pixn, L"SourceY", reinterpret_cast<DWORD*>(&pTheme->nSourceY));
1971 if (S_FALSE == hr)
1972 {
1973 pTheme->nSourceY = -1;
1974 }
1975 ThmExitOnFailure(hr, "Failed to get window SourceY attribute.");
1976
1977 // Parse the optional window style.
1978 hr = XmlGetAttributeNumberBase(pixn, L"HexStyle", 16, &pTheme->dwStyle);
1979 ThmExitOnFailure(hr, "Failed to get theme window style (Window@HexStyle) attribute.");
1980
1981 if (S_FALSE == hr)
1982 {
1983 pTheme->dwStyle = WS_VISIBLE | WS_MINIMIZEBOX | WS_SYSMENU;
1984 pTheme->dwStyle |= (0 <= pTheme->nSourceX && 0 <= pTheme->nSourceY) ? WS_POPUP : WS_OVERLAPPED;
1985 }
1986
1987 hr = XmlGetAttributeNumber(pixn, L"StringId", reinterpret_cast<DWORD*>(&pTheme->uStringId));
1988 ThmExitOnFailure(hr, "Failed to get window StringId attribute.");
1989
1990 if (S_FALSE == hr)
1991 {
1992 pTheme->uStringId = UINT_MAX;
1993
1994 hr = XmlGetAttribute(pixn, L"Caption", &bstr);
1995 ThmExitOnFailure(hr, "Failed to get window Caption attribute.");
1996
1997 if (S_FALSE == hr)
1998 {
1999 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
2000 ThmExitOnRootFailure(hr, "Window elements must contain the Caption or StringId attribute.");
2001 }
2002
2003 hr = StrAllocString(&pTheme->sczCaption, bstr, 0);
2004 ThmExitOnFailure(hr, "Failed to copy window Caption attribute.");
2005 }
2006
2007 // Parse any image lists.
2008 hr = ParseImageLists(hModule, wzRelativePath, pixn, pTheme);
2009 ThmExitOnFailure(hr, "Failed to parse image lists.");
2010
2011 // Parse the pages.
2012 hr = ParsePages(hModule, wzRelativePath, pixn, pTheme);
2013 ThmExitOnFailure(hr, "Failed to parse theme pages.");
2014
2015 // Parse the non-paged controls.
2016 hr = ParseControls(hModule, wzRelativePath, pixn, pTheme, NULL, NULL);
2017 ThmExitOnFailure(hr, "Failed to parse theme controls.");
2018
2019LExit:
2020 ReleaseStr(sczIconFile);
2021 ReleaseBSTR(bstr);
2022 ReleaseObject(pixn);
2023
2024 return hr;
2025}
2026
2027
2028static HRESULT ParseFonts(
2029 __in IXMLDOMElement* pElement,
2030 __in THEME* pTheme
2031 )
2032{
2033 HRESULT hr = S_OK;
2034 IXMLDOMNodeList* pixnl = NULL;
2035 IXMLDOMNode* pixn = NULL;
2036 BSTR bstrName = NULL;
2037 DWORD dwId = 0;
2038 COLORREF crForeground = THEME_INVISIBLE_COLORREF;
2039 COLORREF crBackground = THEME_INVISIBLE_COLORREF;
2040 DWORD dwSystemForegroundColor = FALSE;
2041 DWORD dwSystemBackgroundColor = FALSE;
2042
2043 hr = XmlSelectNodes(pElement, L"Font", &pixnl);
2044 ThmExitOnFailure(hr, "Failed to find font elements.");
2045
2046 hr = pixnl->get_length(reinterpret_cast<long*>(&pTheme->cFonts));
2047 ThmExitOnFailure(hr, "Failed to count the number of theme fonts.");
2048
2049 if (!pTheme->cFonts)
2050 {
2051 ExitFunction1(hr = S_OK);
2052 }
2053
2054 pTheme->rgFonts = static_cast<THEME_FONT*>(MemAlloc(sizeof(THEME_FONT) * pTheme->cFonts, TRUE));
2055 ThmExitOnNull(pTheme->rgFonts, hr, E_OUTOFMEMORY, "Failed to allocate theme fonts.");
2056
2057 while (S_OK == (hr = XmlNextElement(pixnl, &pixn, NULL)))
2058 {
2059 hr = XmlGetAttributeNumber(pixn, L"Id", &dwId);
2060 if (S_FALSE == hr)
2061 {
2062 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
2063 }
2064 ThmExitOnFailure(hr, "Failed to find font id.");
2065
2066 if (pTheme->cFonts <= dwId)
2067 {
2068 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
2069 ThmExitOnRootFailure(hr, "Invalid theme font id.");
2070 }
2071
2072 THEME_FONT* pFont = pTheme->rgFonts + dwId;
2073 if (pFont->cFontInstances)
2074 {
2075 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
2076 ThmExitOnRootFailure(hr, "Theme font id duplicated.");
2077 }
2078
2079 pFont->lfQuality = CLEARTYPE_QUALITY;
2080
2081 hr = XmlGetText(pixn, &bstrName);
2082 if (S_FALSE == hr)
2083 {
2084 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
2085 }
2086 ThmExitOnFailure(hr, "Failed to get font name.");
2087
2088 hr = StrAllocString(&pFont->sczFaceName, bstrName, 0);
2089 ThmExitOnFailure(hr, "Failed to copy font name.");
2090
2091 hr = XmlGetAttributeNumber(pixn, L"Height", reinterpret_cast<DWORD*>(&pFont->lfHeight));
2092 if (S_FALSE == hr)
2093 {
2094 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
2095 }
2096 ThmExitOnFailure(hr, "Failed to find font height attribute.");
2097
2098 hr = XmlGetAttributeNumber(pixn, L"Weight", reinterpret_cast<DWORD*>(&pFont->lfWeight));
2099 if (S_FALSE == hr)
2100 {
2101 pFont->lfWeight = FW_DONTCARE;
2102 hr = S_OK;
2103 }
2104 ThmExitOnFailure(hr, "Failed to find font weight attribute.");
2105
2106 hr = XmlGetYesNoAttribute(pixn, L"Underline", reinterpret_cast<BOOL*>(&pFont->lfUnderline));
2107 if (E_NOTFOUND == hr)
2108 {
2109 pFont->lfUnderline = FALSE;
2110 hr = S_OK;
2111 }
2112 ThmExitOnFailure(hr, "Failed to find font underline attribute.");
2113
2114 hr = GetFontColor(pixn, L"Foreground", &crForeground, &dwSystemForegroundColor);
2115 ThmExitOnFailure(hr, "Failed to find font foreground color.");
2116
2117 hr = GetFontColor(pixn, L"Background", &crBackground, &dwSystemBackgroundColor);
2118 ThmExitOnFailure(hr, "Failed to find font background color.");
2119
2120 pFont->crForeground = crForeground;
2121 if (THEME_INVISIBLE_COLORREF != pFont->crForeground)
2122 {
2123 pFont->hForeground = dwSystemForegroundColor ? ::GetSysColorBrush(dwSystemForegroundColor) : ::CreateSolidBrush(pFont->crForeground);
2124 ThmExitOnNull(pFont->hForeground, hr, E_OUTOFMEMORY, "Failed to create text foreground brush.");
2125 }
2126
2127 pFont->crBackground = crBackground;
2128 if (THEME_INVISIBLE_COLORREF != pFont->crBackground)
2129 {
2130 pFont->hBackground = dwSystemBackgroundColor ? ::GetSysColorBrush(dwSystemBackgroundColor) : ::CreateSolidBrush(pFont->crBackground);
2131 ThmExitOnNull(pFont->hBackground, hr, E_OUTOFMEMORY, "Failed to create text background brush.");
2132 }
2133
2134 ReleaseNullBSTR(bstrName);
2135 ReleaseNullObject(pixn);
2136 }
2137 ThmExitOnFailure(hr, "Failed to enumerate all fonts.");
2138
2139 if (S_FALSE == hr)
2140 {
2141 hr = S_OK;
2142 }
2143
2144LExit:
2145 ReleaseBSTR(bstrName);
2146 ReleaseObject(pixn);
2147 ReleaseObject(pixnl);
2148
2149 return hr;
2150}
2151
2152
2153static HRESULT GetFontColor(
2154 __in IXMLDOMNode* pixn,
2155 __in_z LPCWSTR wzAttributeName,
2156 __out COLORREF* pColorRef,
2157 __out DWORD* pdwSystemColor
2158 )
2159{
2160 HRESULT hr = S_OK;
2161 BSTR bstr = NULL;
2162
2163 *pdwSystemColor = 0;
2164
2165 hr = XmlGetAttribute(pixn, wzAttributeName, &bstr);
2166 if (S_FALSE == hr)
2167 {
2168 *pColorRef = THEME_INVISIBLE_COLORREF;
2169 ExitFunction1(hr = S_OK);
2170 }
2171 ThmExitOnFailure(hr, "Failed to find font %ls color.", wzAttributeName);
2172
2173 if (pdwSystemColor)
2174 {
2175 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"btnface", -1))
2176 {
2177 *pdwSystemColor = COLOR_BTNFACE;
2178 }
2179 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"btntext", -1))
2180 {
2181 *pdwSystemColor = COLOR_BTNTEXT;
2182 }
2183 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"graytext", -1))
2184 {
2185 *pdwSystemColor = COLOR_GRAYTEXT;
2186 }
2187 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"highlight", -1))
2188 {
2189 *pdwSystemColor = COLOR_HIGHLIGHT;
2190 }
2191 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"highlighttext", -1))
2192 {
2193 *pdwSystemColor = COLOR_HIGHLIGHTTEXT;
2194 }
2195 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"hotlight", -1))
2196 {
2197 *pdwSystemColor = COLOR_HOTLIGHT;
2198 }
2199 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"window", -1))
2200 {
2201 *pdwSystemColor = COLOR_WINDOW;
2202 }
2203 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstr, -1, L"windowtext", -1))
2204 {
2205 *pdwSystemColor = COLOR_WINDOWTEXT;
2206 }
2207 else
2208 {
2209 *pColorRef = wcstoul(bstr, NULL, 16);
2210 }
2211
2212 if (*pdwSystemColor)
2213 {
2214 *pColorRef = ::GetSysColor(*pdwSystemColor);
2215 }
2216 }
2217
2218LExit:
2219 ReleaseBSTR(bstr);
2220
2221 return hr;
2222}
2223
2224static HRESULT ParsePages(
2225 __in_opt HMODULE hModule,
2226 __in_opt LPCWSTR wzRelativePath,
2227 __in IXMLDOMNode* pElement,
2228 __in THEME* pTheme
2229 )
2230{
2231 HRESULT hr = S_OK;
2232 IXMLDOMNodeList* pixnl = NULL;
2233 IXMLDOMNode* pixn = NULL;
2234 BSTR bstrType = NULL;
2235 THEME_PAGE* pPage = NULL;
2236 DWORD iPage = 0;
2237
2238 hr = XmlSelectNodes(pElement, L"Page", &pixnl);
2239 ThmExitOnFailure(hr, "Failed to find page elements.");
2240
2241 hr = pixnl->get_length(reinterpret_cast<long*>(&pTheme->cPages));
2242 ThmExitOnFailure(hr, "Failed to count the number of theme pages.");
2243
2244 if (!pTheme->cPages)
2245 {
2246 ExitFunction1(hr = S_OK);
2247 }
2248
2249 pTheme->rgPages = static_cast<THEME_PAGE*>(MemAlloc(sizeof(THEME_PAGE) * pTheme->cPages, TRUE));
2250 ThmExitOnNull(pTheme->rgPages, hr, E_OUTOFMEMORY, "Failed to allocate theme pages.");
2251
2252 while (S_OK == (hr = XmlNextElement(pixnl, &pixn, &bstrType)))
2253 {
2254 pPage = pTheme->rgPages + iPage;
2255
2256 pPage->wId = static_cast<WORD>(iPage + 1);
2257
2258 hr = XmlGetAttributeEx(pixn, L"Name", &pPage->sczName);
2259 if (E_NOTFOUND == hr)
2260 {
2261 hr = S_OK;
2262 }
2263 ThmExitOnFailure(hr, "Failed when querying page Name.");
2264
2265 hr = ParseControls(hModule, wzRelativePath, pixn, pTheme, NULL, pPage);
2266 ThmExitOnFailure(hr, "Failed to parse page controls.");
2267
2268 ++iPage;
2269
2270 ReleaseNullBSTR(bstrType);
2271 ReleaseNullObject(pixn);
2272 }
2273 ThmExitOnFailure(hr, "Failed to enumerate all pages.");
2274
2275 if (S_FALSE == hr)
2276 {
2277 hr = S_OK;
2278 }
2279
2280LExit:
2281 ReleaseBSTR(bstrType);
2282 ReleaseObject(pixn);
2283 ReleaseObject(pixnl);
2284
2285 return hr;
2286}
2287
2288
2289static HRESULT ParseImageLists(
2290 __in_opt HMODULE hModule,
2291 __in_opt LPCWSTR wzRelativePath,
2292 __in IXMLDOMNode* pElement,
2293 __in THEME* pTheme
2294 )
2295{
2296 HRESULT hr = S_OK;
2297 IXMLDOMNodeList* pixnlImageLists = NULL;
2298 IXMLDOMNode* pixnImageList = NULL;
2299 IXMLDOMNodeList* pixnlImages = NULL;
2300 IXMLDOMNode* pixnImage = NULL;
2301 DWORD dwImageListIndex = 0;
2302 DWORD dwImageCount = 0;
2303 HBITMAP hBitmap = NULL;
2304 BITMAP bm = { };
2305 BSTR bstr = NULL;
2306 DWORD i = 0;
2307 int iRetVal = 0;
2308
2309 hr = XmlSelectNodes(pElement, L"ImageList", &pixnlImageLists);
2310 ThmExitOnFailure(hr, "Failed to find ImageList elements.");
2311
2312 hr = pixnlImageLists->get_length(reinterpret_cast<long*>(&pTheme->cImageLists));
2313 ThmExitOnFailure(hr, "Failed to count the number of image lists.");
2314
2315 if (!pTheme->cImageLists)
2316 {
2317 ExitFunction1(hr = S_OK);
2318 }
2319
2320 pTheme->rgImageLists = static_cast<THEME_IMAGELIST*>(MemAlloc(sizeof(THEME_IMAGELIST) * pTheme->cImageLists, TRUE));
2321 ThmExitOnNull(pTheme->rgImageLists, hr, E_OUTOFMEMORY, "Failed to allocate theme image lists.");
2322
2323 while (S_OK == (hr = XmlNextElement(pixnlImageLists, &pixnImageList, NULL)))
2324 {
2325 hr = XmlGetAttribute(pixnImageList, L"Name", &bstr);
2326 if (S_FALSE == hr)
2327 {
2328 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
2329 }
2330 ThmExitOnFailure(hr, "Failed to find ImageList/@Name attribute.");
2331
2332 hr = StrAllocString(&pTheme->rgImageLists[dwImageListIndex].sczName, bstr, 0);
2333 ThmExitOnFailure(hr, "Failed to make copy of ImageList name.");
2334
2335 hr = XmlSelectNodes(pixnImageList, L"Image", &pixnlImages);
2336 ThmExitOnFailure(hr, "Failed to select child Image nodes.");
2337
2338 hr = pixnlImages->get_length(reinterpret_cast<long*>(&dwImageCount));
2339 ThmExitOnFailure(hr, "Failed to count the number of images in list.");
2340
2341 if (0 < dwImageCount)
2342 {
2343 i = 0;
2344 while (S_OK == (hr = XmlNextElement(pixnlImages, &pixnImage, NULL)))
2345 {
2346 if (hBitmap)
2347 {
2348 ::DeleteObject(hBitmap);
2349 hBitmap = NULL;
2350 }
2351 hr = ParseImage(hModule, wzRelativePath, pixnImage, &hBitmap);
2352 ThmExitOnFailure(hr, "Failed to parse image: %u", i);
2353
2354 if (0 == i)
2355 {
2356 ::GetObjectW(hBitmap, sizeof(BITMAP), &bm);
2357
2358 pTheme->rgImageLists[dwImageListIndex].hImageList = ImageList_Create(bm.bmWidth, bm.bmHeight, ILC_COLOR24, dwImageCount, 0);
2359 ThmExitOnNullWithLastError(pTheme->rgImageLists[dwImageListIndex].hImageList, hr, "Failed to create image list.");
2360 }
2361
2362 iRetVal = ImageList_Add(pTheme->rgImageLists[dwImageListIndex].hImageList, hBitmap, NULL);
2363 if (-1 == iRetVal)
2364 {
2365 ThmExitWithLastError(hr, "Failed to add image %u to image list.", i);
2366 }
2367
2368 ++i;
2369 }
2370 }
2371 ++dwImageListIndex;
2372 }
2373
2374LExit:
2375 if (hBitmap)
2376 {
2377 ::DeleteObject(hBitmap);
2378 }
2379 ReleaseBSTR(bstr);
2380 ReleaseObject(pixnlImageLists);
2381 ReleaseObject(pixnImageList);
2382 ReleaseObject(pixnlImages);
2383 ReleaseObject(pixnImage);
2384
2385 return hr;
2386}
2387
2388static void GetControls(
2389 __in THEME* pTheme,
2390 __in_opt THEME_CONTROL* pParentControl,
2391 __out DWORD** ppcControls,
2392 __out THEME_CONTROL*** pprgControls
2393 )
2394{
2395 if (pParentControl)
2396 {
2397 *ppcControls = &pParentControl->cControls;
2398 *pprgControls = &pParentControl->rgControls;
2399 }
2400 else
2401 {
2402 *ppcControls = &pTheme->cControls;
2403 *pprgControls = &pTheme->rgControls;
2404 }
2405}
2406
2407static void GetControls(
2408 __in const THEME* pTheme,
2409 __in_opt const THEME_CONTROL* pParentControl,
2410 __out DWORD& cControls,
2411 __out THEME_CONTROL*& rgControls
2412 )
2413{
2414 if (pParentControl)
2415 {
2416 cControls = pParentControl->cControls;
2417 rgControls = pParentControl->rgControls;
2418 }
2419 else
2420 {
2421 cControls = pTheme->cControls;
2422 rgControls = pTheme->rgControls;
2423 }
2424}
2425
2426static HRESULT ParseControls(
2427 __in_opt HMODULE hModule,
2428 __in_opt LPCWSTR wzRelativePath,
2429 __in IXMLDOMNode* pElement,
2430 __in THEME* pTheme,
2431 __in_opt THEME_CONTROL* pParentControl,
2432 __in_opt THEME_PAGE* pPage
2433 )
2434{
2435 HRESULT hr = S_OK;
2436 IXMLDOMNodeList* pixnl = NULL;
2437 IXMLDOMNode* pixn = NULL;
2438 BSTR bstrType = NULL;
2439 DWORD cNewControls = 0;
2440 DWORD iControl = 0;
2441 DWORD iPageControl = 0;
2442 DWORD* pcControls = NULL;
2443 THEME_CONTROL** prgControls = NULL;
2444
2445 GetControls(pTheme, pParentControl, &pcControls, &prgControls);
2446
2447 hr = ParseRadioButtons(hModule, wzRelativePath, pElement, pTheme, pParentControl, pPage);
2448 ThmExitOnFailure(hr, "Failed to parse radio buttons.");
2449
2450 hr = XmlSelectNodes(pElement, L"Billboard|Button|Checkbox|Combobox|CommandLink|Editbox|Hyperlink|Hypertext|ImageControl|Label|ListView|Panel|Progressbar|Richedit|Static|Tabs|TreeView", &pixnl);
2451 ThmExitOnFailure(hr, "Failed to find control elements.");
2452
2453 hr = pixnl->get_length(reinterpret_cast<long*>(&cNewControls));
2454 ThmExitOnFailure(hr, "Failed to count the number of theme controls.");
2455
2456 if (!cNewControls)
2457 {
2458 ExitFunction1(hr = S_OK);
2459 }
2460
2461 hr = MemReAllocArray(reinterpret_cast<LPVOID*>(prgControls), *pcControls, sizeof(THEME_CONTROL), cNewControls);
2462 ThmExitOnFailure(hr, "Failed to reallocate theme controls.");
2463
2464 cNewControls += *pcControls;
2465
2466 if (pPage)
2467 {
2468 iPageControl = pPage->cControlIndices;
2469 pPage->cControlIndices += cNewControls;
2470 }
2471
2472 iControl = *pcControls;
2473 *pcControls = cNewControls;
2474
2475 while (S_OK == (hr = XmlNextElement(pixnl, &pixn, &bstrType)))
2476 {
2477 THEME_CONTROL_TYPE type = THEME_CONTROL_TYPE_UNKNOWN;
2478
2479 if (!bstrType)
2480 {
2481 hr = E_UNEXPECTED;
2482 ThmExitOnFailure(hr, "Null element encountered!");
2483 }
2484
2485 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Billboard", -1))
2486 {
2487 type = THEME_CONTROL_TYPE_BILLBOARD;
2488 }
2489 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Button", -1))
2490 {
2491 type = THEME_CONTROL_TYPE_BUTTON;
2492 }
2493 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Checkbox", -1))
2494 {
2495 type = THEME_CONTROL_TYPE_CHECKBOX;
2496 }
2497 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Combobox", -1))
2498 {
2499 type = THEME_CONTROL_TYPE_COMBOBOX;
2500 }
2501 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"CommandLink", -1))
2502 {
2503 type = THEME_CONTROL_TYPE_COMMANDLINK;
2504 }
2505 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Editbox", -1))
2506 {
2507 type = THEME_CONTROL_TYPE_EDITBOX;
2508 }
2509 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Hyperlink", -1))
2510 {
2511 type = THEME_CONTROL_TYPE_HYPERLINK;
2512 }
2513 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Hypertext", -1))
2514 {
2515 type = THEME_CONTROL_TYPE_HYPERTEXT;
2516 }
2517 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"ImageControl", -1))
2518 {
2519 type = THEME_CONTROL_TYPE_IMAGE;
2520 }
2521 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Label", -1))
2522 {
2523 type = THEME_CONTROL_TYPE_LABEL;
2524 }
2525 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"ListView", -1))
2526 {
2527 type = THEME_CONTROL_TYPE_LISTVIEW;
2528 }
2529 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Panel", -1))
2530 {
2531 type = THEME_CONTROL_TYPE_PANEL;
2532 }
2533 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Progressbar", -1))
2534 {
2535 type = THEME_CONTROL_TYPE_PROGRESSBAR;
2536 }
2537 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Richedit", -1))
2538 {
2539 type = THEME_CONTROL_TYPE_RICHEDIT;
2540 }
2541 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Static", -1))
2542 {
2543 type = THEME_CONTROL_TYPE_STATIC;
2544 }
2545 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"Tabs", -1))
2546 {
2547 type = THEME_CONTROL_TYPE_TAB;
2548 }
2549 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"TreeView", -1))
2550 {
2551 type = THEME_CONTROL_TYPE_TREEVIEW;
2552 }
2553
2554 if (THEME_CONTROL_TYPE_UNKNOWN != type)
2555 {
2556 THEME_CONTROL* pControl = *prgControls + iControl;
2557 pControl->type = type;
2558
2559 // billboard children are always the size of the billboard
2560 BOOL fBillboardSizing = pParentControl && THEME_CONTROL_TYPE_BILLBOARD == pParentControl->type;
2561
2562 hr = ParseControl(hModule, wzRelativePath, pixn, pTheme, pControl, fBillboardSizing, pPage);
2563 ThmExitOnFailure(hr, "Failed to parse control.");
2564
2565 if (fBillboardSizing)
2566 {
2567 pControl->nX = pControl->nDefaultDpiX = 0;
2568 pControl->nY = pControl->nDefaultDpiY = 0;
2569 pControl->nWidth = pControl->nDefaultDpiWidth = 0;
2570 pControl->nHeight = pControl->nDefaultDpiHeight = 0;
2571 }
2572
2573 if (pPage)
2574 {
2575 pControl->wPageId = pPage->wId;
2576 ++iPageControl;
2577 }
2578
2579 ++iControl;
2580 }
2581
2582 ReleaseNullBSTR(bstrType);
2583 ReleaseNullObject(pixn);
2584 }
2585 ThmExitOnFailure(hr, "Failed to enumerate all controls.");
2586
2587 if (S_FALSE == hr)
2588 {
2589 hr = S_OK;
2590 }
2591
2592 AssertSz(iControl == cNewControls, "The number of parsed controls didn't match the number of expected controls.");
2593
2594LExit:
2595 ReleaseBSTR(bstrType);
2596 ReleaseObject(pixn);
2597 ReleaseObject(pixnl);
2598
2599 return hr;
2600}
2601
2602
2603static HRESULT ParseControl(
2604 __in_opt HMODULE hModule,
2605 __in_opt LPCWSTR wzRelativePath,
2606 __in IXMLDOMNode* pixn,
2607 __in THEME* pTheme,
2608 __in THEME_CONTROL* pControl,
2609 __in BOOL fSkipDimensions,
2610 __in_opt THEME_PAGE* pPage
2611 )
2612{
2613 HRESULT hr = S_OK;
2614 DWORD dwValue = 0;
2615 BOOL fValue = FALSE;
2616 BSTR bstrText = NULL;
2617 BOOL fAnyTextChildren = FALSE;
2618 BOOL fAnyNoteChildren = FALSE;
2619
2620 hr = XmlGetAttributeEx(pixn, L"Name", &pControl->sczName);
2621 if (E_NOTFOUND == hr)
2622 {
2623 hr = S_OK;
2624 }
2625 ThmExitOnFailure(hr, "Failed when querying control Name attribute.");
2626
2627 hr = XmlGetAttributeEx(pixn, L"EnableCondition", &pControl->sczEnableCondition);
2628 if (E_NOTFOUND == hr)
2629 {
2630 hr = S_OK;
2631 }
2632 ThmExitOnFailure(hr, "Failed when querying control EnableCondition attribute.");
2633
2634 hr = XmlGetAttributeEx(pixn, L"VisibleCondition", &pControl->sczVisibleCondition);
2635 if (E_NOTFOUND == hr)
2636 {
2637 hr = S_OK;
2638 }
2639 ThmExitOnFailure(hr, "Failed when querying control VisibleCondition attribute.");
2640
2641 if (!fSkipDimensions)
2642 {
2643 hr = XmlGetAttributeNumber(pixn, L"X", &dwValue);
2644 if (S_FALSE == hr)
2645 {
2646 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
2647 }
2648 ThmExitOnFailure(hr, "Failed to find control X attribute.");
2649
2650 pControl->nX = pControl->nDefaultDpiX = dwValue;
2651
2652 hr = XmlGetAttributeNumber(pixn, L"Y", &dwValue);
2653 if (S_FALSE == hr)
2654 {
2655 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
2656 }
2657 ThmExitOnFailure(hr, "Failed to find control Y attribute.");
2658
2659 pControl->nY = pControl->nDefaultDpiY = dwValue;
2660
2661 hr = XmlGetAttributeNumber(pixn, L"Height", &dwValue);
2662 if (S_FALSE == hr)
2663 {
2664 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
2665 }
2666 ThmExitOnFailure(hr, "Failed to find control Height attribute.");
2667
2668 pControl->nHeight = pControl->nDefaultDpiHeight = dwValue;
2669
2670 hr = XmlGetAttributeNumber(pixn, L"Width", &dwValue);
2671 if (S_FALSE == hr)
2672 {
2673 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
2674 }
2675 ThmExitOnFailure(hr, "Failed to find control Width attribute.");
2676
2677 pControl->nWidth = pControl->nDefaultDpiWidth = dwValue;
2678 }
2679
2680 // Parse the optional background resource image.
2681 hr = ParseImage(hModule, wzRelativePath, pixn, &pControl->hImage);
2682 ThmExitOnFailure(hr, "Failed while parsing control image.");
2683
2684 hr = XmlGetAttributeNumber(pixn, L"SourceX", reinterpret_cast<DWORD*>(&pControl->nSourceX));
2685 if (S_FALSE == hr)
2686 {
2687 pControl->nSourceX = -1;
2688 }
2689 ThmExitOnFailure(hr, "Failed when querying control SourceX attribute.");
2690
2691 hr = XmlGetAttributeNumber(pixn, L"SourceY", reinterpret_cast<DWORD*>(&pControl->nSourceY));
2692 if (S_FALSE == hr)
2693 {
2694 pControl->nSourceY = -1;
2695 }
2696 ThmExitOnFailure(hr, "Failed when querying control SourceY attribute.");
2697
2698 hr = XmlGetAttributeNumber(pixn, L"FontId", &pControl->dwFontId);
2699 if (S_FALSE == hr)
2700 {
2701 pControl->dwFontId = THEME_INVALID_ID;
2702 }
2703 ThmExitOnFailure(hr, "Failed when querying control FontId attribute.");
2704
2705 // Parse the optional window style.
2706 hr = XmlGetAttributeNumberBase(pixn, L"HexStyle", 16, &pControl->dwStyle);
2707 ThmExitOnFailure(hr, "Failed when querying control HexStyle attribute.");
2708
2709 // Parse the tabstop bit "shortcut nomenclature", this could have been set with the style above.
2710 hr = XmlGetYesNoAttribute(pixn, L"TabStop", &fValue);
2711 if (E_NOTFOUND == hr)
2712 {
2713 hr = S_OK;
2714 }
2715 else
2716 {
2717 ThmExitOnFailure(hr, "Failed when querying control TabStop attribute.");
2718
2719 if (fValue)
2720 {
2721 pControl->dwStyle |= WS_TABSTOP;
2722 }
2723 }
2724
2725 hr = XmlGetYesNoAttribute(pixn, L"Visible", &fValue);
2726 if (E_NOTFOUND == hr)
2727 {
2728 hr = S_OK;
2729 }
2730 else
2731 {
2732 ThmExitOnFailure(hr, "Failed when querying control Visible attribute.");
2733
2734 if (fValue)
2735 {
2736 pControl->dwStyle |= WS_VISIBLE;
2737 }
2738 }
2739
2740 hr = XmlGetYesNoAttribute(pixn, L"HideWhenDisabled", &fValue);
2741 if (E_NOTFOUND == hr)
2742 {
2743 hr = S_OK;
2744 }
2745 else
2746 {
2747 ThmExitOnFailure(hr, "Failed when querying control HideWhenDisabled attribute.");
2748
2749 if (fValue)
2750 {
2751 pControl->dwInternalStyle |= INTERNAL_CONTROL_STYLE_HIDE_WHEN_DISABLED;
2752 }
2753 }
2754
2755 hr = XmlGetYesNoAttribute(pixn, L"DisableAutomaticBehavior", &pControl->fDisableVariableFunctionality);
2756 if (E_NOTFOUND == hr)
2757 {
2758 hr = S_OK;
2759 }
2760 else
2761 {
2762 ThmExitOnFailure(hr, "Failed when querying control DisableAutomaticBehavior attribute.");
2763 }
2764
2765 hr = ParseActions(pixn, pControl);
2766 ThmExitOnFailure(hr, "Failed to parse action nodes of the control.");
2767
2768 hr = ParseText(pixn, pControl, &fAnyTextChildren);
2769 ThmExitOnFailure(hr, "Failed to parse text nodes of the control.");
2770
2771 hr = ParseTooltips(pixn, pControl, &fAnyTextChildren);
2772 ThmExitOnFailure(hr, "Failed to parse control Tooltip.");
2773
2774 if (THEME_CONTROL_TYPE_COMMANDLINK == pControl->type)
2775 {
2776 hr = ParseNotes(pixn, pControl, &fAnyNoteChildren);
2777 ThmExitOnFailure(hr, "Failed to parse note text nodes of the control.");
2778 }
2779
2780 if (fAnyTextChildren || fAnyNoteChildren)
2781 {
2782 pControl->uStringId = UINT_MAX;
2783 }
2784 else
2785 {
2786 hr = XmlGetAttributeNumber(pixn, L"StringId", reinterpret_cast<DWORD*>(&pControl->uStringId));
2787 ThmExitOnFailure(hr, "Failed when querying control StringId attribute.");
2788
2789 if (S_FALSE == hr)
2790 {
2791 pControl->uStringId = UINT_MAX;
2792
2793 if (THEME_CONTROL_TYPE_BILLBOARD == pControl->type || THEME_CONTROL_TYPE_PANEL == pControl->type)
2794 {
2795 // Billboards and panels have child elements and we don't want to pick up child element text in the parents.
2796 hr = S_OK;
2797 }
2798 else
2799 {
2800 hr = XmlGetText(pixn, &bstrText);
2801 ThmExitOnFailure(hr, "Failed to get control inner text.");
2802
2803 if (S_OK == hr)
2804 {
2805 hr = StrAllocString(&pControl->sczText, bstrText, 0);
2806 ThmExitOnFailure(hr, "Failed to copy control text.");
2807
2808 ReleaseNullBSTR(bstrText);
2809 }
2810 else if (S_FALSE == hr)
2811 {
2812 hr = S_OK;
2813 }
2814 }
2815 }
2816 }
2817
2818 if (THEME_CONTROL_TYPE_BILLBOARD == pControl->type)
2819 {
2820 hr = XmlGetYesNoAttribute(pixn, L"Loop", &pControl->fBillboardLoops);
2821 if (E_NOTFOUND == hr)
2822 {
2823 hr = S_OK;
2824 }
2825 ThmExitOnFailure(hr, "Failed when querying Billboard/@Loop attribute.");
2826
2827 pControl->wBillboardInterval = 5000;
2828 hr = XmlGetAttributeNumber(pixn, L"Interval", &dwValue);
2829 if (S_OK == hr && dwValue)
2830 {
2831 pControl->wBillboardInterval = static_cast<WORD>(dwValue & 0xFFFF);
2832 }
2833 ThmExitOnFailure(hr, "Failed when querying Billboard/@Interval attribute.");
2834
2835 hr = ParseControls(hModule, wzRelativePath, pixn, pTheme, pControl, pPage);
2836 ThmExitOnFailure(hr, "Failed to parse billboard children.");
2837 }
2838 else if (THEME_CONTROL_TYPE_COMMANDLINK == pControl->type)
2839 {
2840 hr = ParseIcon(hModule, wzRelativePath, pixn, &pControl->hIcon);
2841 ThmExitOnFailure(hr, "Failed while parsing control icon.");
2842 }
2843 else if (THEME_CONTROL_TYPE_EDITBOX == pControl->type)
2844 {
2845 hr = XmlGetYesNoAttribute(pixn, L"FileSystemAutoComplete", &fValue);
2846 if (E_NOTFOUND == hr)
2847 {
2848 hr = S_OK;
2849 }
2850 else
2851 {
2852 ThmExitOnFailure(hr, "Failed when querying Editbox/@FileSystemAutoComplete attribute.");
2853
2854 if (fValue)
2855 {
2856 pControl->dwInternalStyle |= INTERNAL_CONTROL_STYLE_FILESYSTEM_AUTOCOMPLETE;
2857 }
2858 }
2859 }
2860 else if (THEME_CONTROL_TYPE_HYPERLINK == pControl->type || THEME_CONTROL_TYPE_BUTTON == pControl->type)
2861 {
2862 hr = XmlGetAttributeNumber(pixn, L"HoverFontId", &pControl->dwFontHoverId);
2863 if (S_FALSE == hr)
2864 {
2865 pControl->dwFontHoverId = THEME_INVALID_ID;
2866 }
2867 ThmExitOnFailure(hr, "Failed when querying control HoverFontId attribute.");
2868
2869 hr = XmlGetAttributeNumber(pixn, L"SelectedFontId", &pControl->dwFontSelectedId);
2870 if (S_FALSE == hr)
2871 {
2872 pControl->dwFontSelectedId = THEME_INVALID_ID;
2873 }
2874 ThmExitOnFailure(hr, "Failed when querying control SelectedFontId attribute.");
2875 }
2876 else if (THEME_CONTROL_TYPE_LABEL == pControl->type)
2877 {
2878 hr = XmlGetYesNoAttribute(pixn, L"Center", &fValue);
2879 if (E_NOTFOUND == hr)
2880 {
2881 hr = S_OK;
2882 }
2883 else if (fValue)
2884 {
2885 pControl->dwStyle |= SS_CENTER;
2886 }
2887 ThmExitOnFailure(hr, "Failed when querying Label/@Center attribute.");
2888
2889 hr = XmlGetYesNoAttribute(pixn, L"DisablePrefix", &fValue);
2890 if (E_NOTFOUND == hr)
2891 {
2892 hr = S_OK;
2893 }
2894 else if (fValue)
2895 {
2896 pControl->dwStyle |= SS_NOPREFIX;
2897 }
2898 ThmExitOnFailure(hr, "Failed when querying Label/@DisablePrefix attribute.");
2899 }
2900 else if (THEME_CONTROL_TYPE_LISTVIEW == pControl->type)
2901 {
2902 // Parse the optional extended window style.
2903 hr = XmlGetAttributeNumberBase(pixn, L"HexExtendedStyle", 16, &pControl->dwExtendedStyle);
2904 ThmExitOnFailure(hr, "Failed when querying ListView/@HexExtendedStyle attribute.");
2905
2906 hr = XmlGetAttribute(pixn, L"ImageList", &bstrText);
2907 if (S_FALSE != hr)
2908 {
2909 ThmExitOnFailure(hr, "Failed when querying ListView/@ImageList attribute.");
2910
2911 hr = FindImageList(pTheme, bstrText, &pControl->rghImageList[0]);
2912 ThmExitOnFailure(hr, "Failed to find image list %ls while setting ImageList for ListView.", bstrText);
2913 }
2914
2915 hr = XmlGetAttribute(pixn, L"ImageListSmall", &bstrText);
2916 if (S_FALSE != hr)
2917 {
2918 ThmExitOnFailure(hr, "Failed when querying ListView/@ImageListSmall attribute.");
2919
2920 hr = FindImageList(pTheme, bstrText, &pControl->rghImageList[1]);
2921 ThmExitOnFailure(hr, "Failed to find image list %ls while setting ImageListSmall for ListView.", bstrText);
2922 }
2923
2924 hr = XmlGetAttribute(pixn, L"ImageListState", &bstrText);
2925 if (S_FALSE != hr)
2926 {
2927 ThmExitOnFailure(hr, "Failed when querying ListView/@ImageListState attribute.");
2928
2929 hr = FindImageList(pTheme, bstrText, &pControl->rghImageList[2]);
2930 ThmExitOnFailure(hr, "Failed to find image list %ls while setting ImageListState for ListView.", bstrText);
2931 }
2932
2933 hr = XmlGetAttribute(pixn, L"ImageListGroupHeader", &bstrText);
2934 if (S_FALSE != hr)
2935 {
2936 ThmExitOnFailure(hr, "Failed when querying ListView/@ImageListGroupHeader attribute.");
2937
2938 hr = FindImageList(pTheme, bstrText, &pControl->rghImageList[3]);
2939 ThmExitOnFailure(hr, "Failed to find image list %ls while setting ImageListGroupHeader for ListView.", bstrText);
2940 }
2941
2942 hr = ParseColumns(pixn, pControl);
2943 ThmExitOnFailure(hr, "Failed to parse columns.");
2944 }
2945 else if (THEME_CONTROL_TYPE_PANEL == pControl->type)
2946 {
2947 hr = ParseControls(hModule, wzRelativePath, pixn, pTheme, pControl, pPage);
2948 ThmExitOnFailure(hr, "Failed to parse panel children.");
2949 }
2950 else if (THEME_CONTROL_TYPE_RADIOBUTTON == pControl->type)
2951 {
2952 hr = XmlGetAttributeEx(pixn, L"Value", &pControl->sczValue);
2953 if (E_NOTFOUND == hr)
2954 {
2955 hr = S_OK;
2956 }
2957 ThmExitOnFailure(hr, "Failed when querying RadioButton/@Value attribute.");
2958 }
2959 else if (THEME_CONTROL_TYPE_TAB == pControl->type)
2960 {
2961 hr = ParseTabs(pixn, pControl);
2962 ThmExitOnFailure(hr, "Failed to parse tabs");
2963 }
2964 else if (THEME_CONTROL_TYPE_TREEVIEW == pControl->type)
2965 {
2966 pControl->dwStyle |= TVS_DISABLEDRAGDROP;
2967
2968 hr = XmlGetYesNoAttribute(pixn, L"EnableDragDrop", &fValue);
2969 if (E_NOTFOUND == hr)
2970 {
2971 hr = S_OK;
2972 }
2973 else if (fValue)
2974 {
2975 pControl->dwStyle &= ~TVS_DISABLEDRAGDROP;
2976 }
2977 ThmExitOnFailure(hr, "Failed when querying TreeView/@EnableDragDrop attribute.");
2978
2979 hr = XmlGetYesNoAttribute(pixn, L"FullRowSelect", &fValue);
2980 if (E_NOTFOUND == hr)
2981 {
2982 hr = S_OK;
2983 }
2984 else if (fValue)
2985 {
2986 pControl->dwStyle |= TVS_FULLROWSELECT;
2987 }
2988 ThmExitOnFailure(hr, "Failed when querying TreeView/@FullRowSelect attribute.");
2989
2990 hr = XmlGetYesNoAttribute(pixn, L"HasButtons", &fValue);
2991 if (E_NOTFOUND == hr)
2992 {
2993 hr = S_OK;
2994 }
2995 else if (fValue)
2996 {
2997 pControl->dwStyle |= TVS_HASBUTTONS;
2998 }
2999 ThmExitOnFailure(hr, "Failed when querying TreeView/@HasButtons attribute.");
3000
3001 hr = XmlGetYesNoAttribute(pixn, L"AlwaysShowSelect", &fValue);
3002 if (E_NOTFOUND == hr)
3003 {
3004 hr = S_OK;
3005 }
3006 else if (fValue)
3007 {
3008 pControl->dwStyle |= TVS_SHOWSELALWAYS;
3009 }
3010 ThmExitOnFailure(hr, "Failed when querying TreeView/@AlwaysShowSelect attribute.");
3011
3012 hr = XmlGetYesNoAttribute(pixn, L"LinesAtRoot", &fValue);
3013 if (E_NOTFOUND == hr)
3014 {
3015 hr = S_OK;
3016 }
3017 else if (fValue)
3018 {
3019 pControl->dwStyle |= TVS_LINESATROOT;
3020 }
3021 ThmExitOnFailure(hr, "Failed when querying TreeView/@LinesAtRoot attribute.");
3022
3023 hr = XmlGetYesNoAttribute(pixn, L"HasLines", &fValue);
3024 if (E_NOTFOUND == hr)
3025 {
3026 hr = S_OK;
3027 }
3028 else if (fValue)
3029 {
3030 pControl->dwStyle |= TVS_HASLINES;
3031 }
3032 ThmExitOnFailure(hr, "Failed when querying TreeView/@HasLines attribute.");
3033 }
3034
3035LExit:
3036 ReleaseBSTR(bstrText);
3037
3038 return hr;
3039}
3040
3041
3042static HRESULT ParseActions(
3043 __in IXMLDOMNode* pixn,
3044 __in THEME_CONTROL* pControl
3045 )
3046{
3047 HRESULT hr = S_OK;
3048 DWORD i = 0;
3049 IXMLDOMNodeList* pixnl = NULL;
3050 IXMLDOMNode* pixnChild = NULL;
3051 BSTR bstrType = NULL;
3052
3053 hr = XmlSelectNodes(pixn, L"BrowseDirectoryAction|ChangePageAction|CloseWindowAction", &pixnl);
3054 ThmExitOnFailure(hr, "Failed to select child action nodes.");
3055
3056 hr = pixnl->get_length(reinterpret_cast<long*>(&pControl->cActions));
3057 ThmExitOnFailure(hr, "Failed to count the number of action nodes.");
3058
3059 if (0 < pControl->cActions)
3060 {
3061 MemAllocArray(reinterpret_cast<LPVOID*>(&pControl->rgActions), sizeof(THEME_ACTION), pControl->cActions);
3062 ThmExitOnNull(pControl->rgActions, hr, E_OUTOFMEMORY, "Failed to allocate THEME_ACTION structs.");
3063
3064 i = 0;
3065 while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, &bstrType)))
3066 {
3067 if (!bstrType)
3068 {
3069 hr = E_UNEXPECTED;
3070 ThmExitOnFailure(hr, "Null element encountered!");
3071 }
3072
3073 THEME_ACTION* pAction = pControl->rgActions + i;
3074
3075 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"BrowseDirectoryAction", -1))
3076 {
3077 pAction->type = THEME_ACTION_TYPE_BROWSE_DIRECTORY;
3078
3079 hr = XmlGetAttributeEx(pixnChild, L"VariableName", &pAction->BrowseDirectory.sczVariableName);
3080 ThmExitOnFailure(hr, "Failed when querying BrowseDirectoryAction/@VariableName attribute.");
3081 }
3082 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"ChangePageAction", -1))
3083 {
3084 pAction->type = THEME_ACTION_TYPE_CHANGE_PAGE;
3085
3086 hr = XmlGetAttributeEx(pixnChild, L"Page", &pAction->ChangePage.sczPageName);
3087 ThmExitOnFailure(hr, "Failed when querying ChangePageAction/@Page attribute.");
3088
3089 hr = XmlGetYesNoAttribute(pixnChild, L"Cancel", &pAction->ChangePage.fCancel);
3090 if (E_NOTFOUND != hr)
3091 {
3092 ThmExitOnFailure(hr, "Failed when querying ChangePageAction/@Cancel attribute.");
3093 }
3094 }
3095 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrType, -1, L"CloseWindowAction", -1))
3096 {
3097 pAction->type = THEME_ACTION_TYPE_CLOSE_WINDOW;
3098 }
3099 else
3100 {
3101 hr = E_UNEXPECTED;
3102 ThmExitOnFailure(hr, "Unexpected element encountered: %ls", bstrType);
3103 }
3104
3105 hr = XmlGetAttributeEx(pixnChild, L"Condition", &pAction->sczCondition);
3106 if (E_NOTFOUND != hr)
3107 {
3108 ThmExitOnFailure(hr, "Failed when querying %ls/@Condition attribute.", bstrType);
3109 }
3110
3111 if (!pAction->sczCondition)
3112 {
3113 if (pControl->pDefaultAction)
3114 {
3115 hr = E_INVALIDDATA;
3116 ThmExitOnFailure(hr, "Control '%ls' has multiple actions without a condition.", pControl->sczName);
3117 }
3118
3119 pControl->pDefaultAction = pAction;
3120 }
3121
3122 ++i;
3123 ReleaseNullBSTR(bstrType);
3124 ReleaseObject(pixnChild);
3125 }
3126 }
3127
3128LExit:
3129 ReleaseObject(pixnl);
3130 ReleaseObject(pixnChild);
3131 ReleaseBSTR(bstrType);
3132
3133 return hr;
3134}
3135
3136
3137static HRESULT ParseColumns(
3138 __in IXMLDOMNode* pixn,
3139 __in THEME_CONTROL* pControl
3140 )
3141{
3142 HRESULT hr = S_OK;
3143 DWORD i = 0;
3144 IXMLDOMNodeList* pixnl = NULL;
3145 IXMLDOMNode* pixnChild = NULL;
3146 BSTR bstrText = NULL;
3147 DWORD dwValue = 0;
3148
3149 hr = XmlSelectNodes(pixn, L"Column", &pixnl);
3150 ThmExitOnFailure(hr, "Failed to select child column nodes.");
3151
3152 hr = pixnl->get_length(reinterpret_cast<long*>(&pControl->cColumns));
3153 ThmExitOnFailure(hr, "Failed to count the number of control columns.");
3154
3155 if (0 < pControl->cColumns)
3156 {
3157 hr = MemAllocArray(reinterpret_cast<LPVOID*>(&pControl->ptcColumns), sizeof(THEME_COLUMN), pControl->cColumns);
3158 ThmExitOnFailure(hr, "Failed to allocate column structs.");
3159
3160 i = 0;
3161 while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, NULL)))
3162 {
3163 THEME_COLUMN* pColumn = pControl->ptcColumns + i;
3164
3165 hr = XmlGetText(pixnChild, &bstrText);
3166 ThmExitOnFailure(hr, "Failed to get inner text of column element.");
3167
3168 hr = XmlGetAttributeNumber(pixnChild, L"Width", &dwValue);
3169 if (S_FALSE == hr)
3170 {
3171 dwValue = 100;
3172 }
3173 ThmExitOnFailure(hr, "Failed to get column width attribute.");
3174
3175 pColumn->nBaseWidth = pColumn->nDefaultDpiBaseWidth = dwValue;
3176
3177 hr = XmlGetYesNoAttribute(pixnChild, L"Expands", reinterpret_cast<BOOL*>(&pColumn->fExpands));
3178 if (E_NOTFOUND == hr)
3179 {
3180 hr = S_OK;
3181 }
3182 ThmExitOnFailure(hr, "Failed to get expands attribute.");
3183
3184 hr = StrAllocString(&pColumn->pszName, bstrText, 0);
3185 ThmExitOnFailure(hr, "Failed to copy column name.");
3186
3187 ++i;
3188 ReleaseNullBSTR(bstrText);
3189 }
3190 }
3191
3192LExit:
3193 ReleaseObject(pixnl);
3194 ReleaseObject(pixnChild);
3195 ReleaseBSTR(bstrText);
3196
3197 return hr;
3198}
3199
3200
3201static HRESULT ParseRadioButtons(
3202 __in_opt HMODULE hModule,
3203 __in_opt LPCWSTR wzRelativePath,
3204 __in IXMLDOMNode* pixn,
3205 __in THEME* pTheme,
3206 __in_opt THEME_CONTROL* pParentControl,
3207 __in THEME_PAGE* pPage
3208 )
3209{
3210 HRESULT hr = S_OK;
3211 DWORD cRadioButtons = 0;
3212 DWORD iControl = 0;
3213 DWORD iPageControl = 0;
3214 IXMLDOMNodeList* pixnlRadioButtons = NULL;
3215 IXMLDOMNodeList* pixnl = NULL;
3216 IXMLDOMNode* pixnRadioButtons = NULL;
3217 IXMLDOMNode* pixnChild = NULL;
3218 LPWSTR sczName = NULL;
3219 THEME_CONTROL* pControl = NULL;
3220 BOOL fFirst = FALSE;
3221 DWORD* pcControls = NULL;
3222 THEME_CONTROL** prgControls = NULL;
3223
3224 GetControls(pTheme, pParentControl, &pcControls, &prgControls);
3225
3226 hr = XmlSelectNodes(pixn, L"RadioButtons", &pixnlRadioButtons);
3227 ThmExitOnFailure(hr, "Failed to select RadioButtons nodes.");
3228
3229 while (S_OK == (hr = XmlNextElement(pixnlRadioButtons, &pixnRadioButtons, NULL)))
3230 {
3231 hr = XmlGetAttributeEx(pixnRadioButtons, L"Name", &sczName);
3232 if (E_NOTFOUND == hr)
3233 {
3234 hr = S_OK;
3235 }
3236 ThmExitOnFailure(hr, "Failed when querying RadioButtons Name.");
3237
3238 hr = XmlSelectNodes(pixnRadioButtons, L"RadioButton", &pixnl);
3239 ThmExitOnFailure(hr, "Failed to select RadioButton nodes.");
3240
3241 hr = pixnl->get_length(reinterpret_cast<long*>(&cRadioButtons));
3242 ThmExitOnFailure(hr, "Failed to count the number of RadioButton nodes.");
3243
3244 if (cRadioButtons)
3245 {
3246 if (pPage)
3247 {
3248 iPageControl = pPage->cControlIndices;
3249 pPage->cControlIndices += cRadioButtons;
3250 }
3251
3252 hr = MemReAllocArray(reinterpret_cast<LPVOID*>(prgControls), *pcControls, sizeof(THEME_CONTROL), cRadioButtons);
3253 ThmExitOnFailure(hr, "Failed to reallocate theme controls.");
3254
3255 iControl = *pcControls;
3256 *pcControls += cRadioButtons;
3257
3258 fFirst = TRUE;
3259
3260 while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, NULL)))
3261 {
3262 pControl = *prgControls + iControl;
3263 pControl->type = THEME_CONTROL_TYPE_RADIOBUTTON;
3264
3265 hr = ParseControl(hModule, wzRelativePath, pixnChild, pTheme, pControl, FALSE, pPage);
3266 ThmExitOnFailure(hr, "Failed to parse control.");
3267
3268 if (fFirst)
3269 {
3270 pControl->dwStyle |= WS_GROUP;
3271 fFirst = FALSE;
3272 }
3273
3274 hr = StrAllocString(&pControl->sczVariable, sczName, 0);
3275 ThmExitOnFailure(hr, "Failed to copy radio button variable.");
3276
3277 if (pPage)
3278 {
3279 pControl->wPageId = pPage->wId;
3280 ++iPageControl;
3281 }
3282
3283 ++iControl;
3284 }
3285
3286 if (!fFirst)
3287 {
3288 pControl->fLastRadioButton = TRUE;
3289 }
3290 }
3291 }
3292
3293LExit:
3294 ReleaseStr(sczName);
3295 ReleaseObject(pixnl);
3296 ReleaseObject(pixnChild);
3297 ReleaseObject(pixnlRadioButtons);
3298 ReleaseObject(pixnRadioButtons);
3299
3300 return hr;
3301}
3302
3303
3304static HRESULT ParseTabs(
3305 __in IXMLDOMNode* pixn,
3306 __in THEME_CONTROL* pControl
3307 )
3308{
3309 HRESULT hr = S_OK;
3310 DWORD i = 0;
3311 IXMLDOMNodeList* pixnl = NULL;
3312 IXMLDOMNode* pixnChild = NULL;
3313 BSTR bstrText = NULL;
3314
3315 hr = XmlSelectNodes(pixn, L"Tab", &pixnl);
3316 ThmExitOnFailure(hr, "Failed to select child tab nodes.");
3317
3318 hr = pixnl->get_length(reinterpret_cast<long*>(&pControl->cTabs));
3319 ThmExitOnFailure(hr, "Failed to count the number of tabs.");
3320
3321 if (0 < pControl->cTabs)
3322 {
3323 hr = MemAllocArray(reinterpret_cast<LPVOID*>(&pControl->pttTabs), sizeof(THEME_TAB), pControl->cTabs);
3324 ThmExitOnFailure(hr, "Failed to allocate tab structs.");
3325
3326 i = 0;
3327 while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, NULL)))
3328 {
3329 hr = XmlGetText(pixnChild, &bstrText);
3330 ThmExitOnFailure(hr, "Failed to get inner text of tab element.");
3331
3332 hr = StrAllocString(&(pControl->pttTabs[i].pszName), bstrText, 0);
3333 ThmExitOnFailure(hr, "Failed to copy tab name.");
3334
3335 ++i;
3336 ReleaseNullBSTR(bstrText);
3337 }
3338 }
3339
3340LExit:
3341 ReleaseObject(pixnl);
3342 ReleaseObject(pixnChild);
3343 ReleaseBSTR(bstrText);
3344
3345 return hr;
3346}
3347
3348
3349static HRESULT ParseText(
3350 __in IXMLDOMNode* pixn,
3351 __in THEME_CONTROL* pControl,
3352 __inout BOOL* pfAnyChildren
3353 )
3354{
3355 HRESULT hr = S_OK;
3356 DWORD i = 0;
3357 IXMLDOMNodeList* pixnl = NULL;
3358 IXMLDOMNode* pixnChild = NULL;
3359 BSTR bstrText = NULL;
3360
3361 hr = XmlSelectNodes(pixn, L"Text", &pixnl);
3362 ThmExitOnFailure(hr, "Failed to select child Text nodes.");
3363
3364 hr = pixnl->get_length(reinterpret_cast<long*>(&pControl->cConditionalText));
3365 ThmExitOnFailure(hr, "Failed to count the number of Text nodes.");
3366
3367 *pfAnyChildren |= 0 < pControl->cConditionalText;
3368
3369 if (0 < pControl->cConditionalText)
3370 {
3371 MemAllocArray(reinterpret_cast<LPVOID*>(&pControl->rgConditionalText), sizeof(THEME_CONDITIONAL_TEXT), pControl->cConditionalText);
3372 ThmExitOnNull(pControl->rgConditionalText, hr, E_OUTOFMEMORY, "Failed to allocate THEME_CONDITIONAL_TEXT structs.");
3373
3374 i = 0;
3375 while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, NULL)))
3376 {
3377 THEME_CONDITIONAL_TEXT* pConditionalText = pControl->rgConditionalText + i;
3378
3379 hr = XmlGetAttributeEx(pixnChild, L"Condition", &pConditionalText->sczCondition);
3380 if (E_NOTFOUND == hr)
3381 {
3382 hr = S_OK;
3383 }
3384 ThmExitOnFailure(hr, "Failed when querying Text/@Condition attribute.");
3385
3386 hr = XmlGetText(pixnChild, &bstrText);
3387 ThmExitOnFailure(hr, "Failed to get inner text of Text element.");
3388
3389 if (S_OK == hr)
3390 {
3391 if (pConditionalText->sczCondition)
3392 {
3393 hr = StrAllocString(&pConditionalText->sczText, bstrText, 0);
3394 ThmExitOnFailure(hr, "Failed to copy text to conditional text.");
3395
3396 ++i;
3397 }
3398 else
3399 {
3400 if (pControl->sczText)
3401 {
3402 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
3403 ThmExitOnFailure(hr, "Unconditional text for the '%ls' control is specified multiple times.", pControl->sczName);
3404 }
3405
3406 hr = StrAllocString(&pControl->sczText, bstrText, 0);
3407 ThmExitOnFailure(hr, "Failed to copy text to control.");
3408
3409 // Unconditional text entries aren't stored in the conditional text list.
3410 --pControl->cConditionalText;
3411 }
3412 }
3413
3414 ReleaseNullBSTR(bstrText);
3415 }
3416 }
3417
3418LExit:
3419 ReleaseObject(pixnl);
3420 ReleaseObject(pixnChild);
3421 ReleaseBSTR(bstrText);
3422
3423 return hr;
3424}
3425
3426
3427static HRESULT ParseTooltips(
3428 __in IXMLDOMNode* pixn,
3429 __in THEME_CONTROL* pControl,
3430 __inout BOOL* pfAnyChildren
3431)
3432{
3433 HRESULT hr = S_OK;
3434 IXMLDOMNode* pixnChild = NULL;
3435 BSTR bstrText = NULL;
3436
3437 hr = XmlSelectSingleNode(pixn, L"Tooltip", &pixnChild);
3438 ThmExitOnFailure(hr, "Failed to select child Tooltip node.");
3439
3440 if (S_OK == hr)
3441 {
3442 *pfAnyChildren |= TRUE;
3443
3444 hr = XmlGetText(pixnChild, &bstrText);
3445 ThmExitOnFailure(hr, "Failed to get inner text of Tooltip element.");
3446
3447 if (S_OK == hr)
3448 {
3449 hr = StrAllocString(&pControl->sczTooltip, bstrText, 0);
3450 ThmExitOnFailure(hr, "Failed to copy tooltip text to control.");
3451 }
3452 }
3453
3454LExit:
3455 ReleaseObject(pixnChild);
3456 ReleaseBSTR(bstrText);
3457
3458 return hr;
3459}
3460
3461
3462static HRESULT ParseNotes(
3463 __in IXMLDOMNode* pixn,
3464 __in THEME_CONTROL* pControl,
3465 __out BOOL* pfAnyChildren
3466 )
3467{
3468 HRESULT hr = S_OK;
3469 DWORD i = 0;
3470 IXMLDOMNodeList* pixnl = NULL;
3471 IXMLDOMNode* pixnChild = NULL;
3472 BSTR bstrText = NULL;
3473
3474 hr = XmlSelectNodes(pixn, L"Note", &pixnl);
3475 ThmExitOnFailure(hr, "Failed to select child Note nodes.");
3476
3477 hr = pixnl->get_length(reinterpret_cast<long*>(&pControl->cConditionalNotes));
3478 ThmExitOnFailure(hr, "Failed to count the number of Note nodes.");
3479
3480 if (pfAnyChildren)
3481 {
3482 *pfAnyChildren = 0 < pControl->cConditionalNotes;
3483 }
3484
3485 if (0 < pControl->cConditionalNotes)
3486 {
3487 MemAllocArray(reinterpret_cast<LPVOID*>(&pControl->rgConditionalNotes), sizeof(THEME_CONDITIONAL_TEXT), pControl->cConditionalNotes);
3488 ThmExitOnNull(pControl->rgConditionalNotes, hr, E_OUTOFMEMORY, "Failed to allocate note THEME_CONDITIONAL_TEXT structs.");
3489
3490 i = 0;
3491 while (S_OK == (hr = XmlNextElement(pixnl, &pixnChild, NULL)))
3492 {
3493 THEME_CONDITIONAL_TEXT* pConditionalNote = pControl->rgConditionalNotes + i;
3494
3495 hr = XmlGetAttributeEx(pixnChild, L"Condition", &pConditionalNote->sczCondition);
3496 if (E_NOTFOUND == hr)
3497 {
3498 hr = S_OK;
3499 }
3500 ThmExitOnFailure(hr, "Failed when querying Note/@Condition attribute.");
3501
3502 hr = XmlGetText(pixnChild, &bstrText);
3503 ThmExitOnFailure(hr, "Failed to get inner text of Note element.");
3504
3505 if (S_OK == hr)
3506 {
3507 if (pConditionalNote->sczCondition)
3508 {
3509 hr = StrAllocString(&pConditionalNote->sczText, bstrText, 0);
3510 ThmExitOnFailure(hr, "Failed to copy text to conditional note text.");
3511
3512 ++i;
3513 }
3514 else
3515 {
3516 if (pControl->sczNote)
3517 {
3518 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
3519 ThmExitOnFailure(hr, "Unconditional note text for the '%ls' control is specified multiple times.", pControl->sczName);
3520 }
3521
3522 hr = StrAllocString(&pControl->sczNote, bstrText, 0);
3523 ThmExitOnFailure(hr, "Failed to copy text to command link control.");
3524
3525 // Unconditional note entries aren't stored in the conditional notes list.
3526 --pControl->cConditionalNotes;
3527 }
3528 }
3529
3530 ReleaseNullBSTR(bstrText);
3531 }
3532 }
3533
3534LExit:
3535 ReleaseObject(pixnl);
3536 ReleaseObject(pixnChild);
3537 ReleaseBSTR(bstrText);
3538
3539 return hr;
3540}
3541
3542
3543static HRESULT StartBillboard(
3544 __in THEME* pTheme,
3545 __in DWORD dwControl
3546 )
3547{
3548 HRESULT hr = E_NOTFOUND;
3549 HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl);
3550
3551 if (hWnd)
3552 {
3553 THEME_CONTROL* pControl = const_cast<THEME_CONTROL*>(FindControlFromHWnd(pTheme, hWnd));
3554 if (pControl && THEME_CONTROL_TYPE_BILLBOARD == pControl->type)
3555 {
3556 // kick off
3557 pControl->dwData = 0;
3558 OnBillboardTimer(pTheme, pTheme->hwndParent, dwControl);
3559
3560 if (!::SetTimer(pTheme->hwndParent, pControl->wId, pControl->wBillboardInterval, NULL))
3561 {
3562 ThmExitWithLastError(hr, "Failed to start billboard.");
3563 }
3564
3565 hr = S_OK;
3566 }
3567 }
3568
3569LExit:
3570 return hr;
3571}
3572
3573
3574static HRESULT StopBillboard(
3575 __in THEME* pTheme,
3576 __in DWORD dwControl
3577 )
3578{
3579 HRESULT hr = E_NOTFOUND;
3580 HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl);
3581
3582 if (hWnd)
3583 {
3584 const THEME_CONTROL* pControl = FindControlFromHWnd(pTheme, hWnd);
3585 if (pControl && THEME_CONTROL_TYPE_BILLBOARD == pControl->type)
3586 {
3587 ThemeControlEnable(pTheme, dwControl, FALSE);
3588
3589 if (::KillTimer(pTheme->hwndParent, pControl->wId))
3590 {
3591 hr = S_OK;
3592 }
3593 }
3594 }
3595
3596 return hr;
3597}
3598
3599static HRESULT EnsureFontInstance(
3600 __in THEME* pTheme,
3601 __in THEME_FONT* pFont,
3602 __out THEME_FONT_INSTANCE** ppFontInstance
3603 )
3604{
3605 HRESULT hr = S_OK;
3606 THEME_FONT_INSTANCE* pFontInstance = NULL;
3607 LOGFONTW lf = { };
3608
3609 for (DWORD i = 0; i < pFont->cFontInstances; ++i)
3610 {
3611 pFontInstance = pFont->rgFontInstances + i;
3612 if (pTheme->nDpi == pFontInstance->nDpi)
3613 {
3614 *ppFontInstance = pFontInstance;
3615 ExitFunction();
3616 }
3617 }
3618
3619 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pFont->rgFontInstances), pFont->cFontInstances, sizeof(THEME_FONT_INSTANCE), GROW_FONT_INSTANCES);
3620 ThmExitOnFailure(hr, "Failed to allocate memory for font instances.");
3621
3622 pFontInstance = pFont->rgFontInstances + pFont->cFontInstances;
3623 pFontInstance->nDpi = pTheme->nDpi;
3624
3625 lf.lfHeight = DpiuScaleValue(pFont->lfHeight, pFontInstance->nDpi);
3626 lf.lfWeight = pFont->lfWeight;
3627 lf.lfUnderline = pFont->lfUnderline;
3628 lf.lfQuality = pFont->lfQuality;
3629
3630 hr = ::StringCchCopyW(lf.lfFaceName, countof(lf.lfFaceName), pFont->sczFaceName);
3631 ThmExitOnFailure(hr, "Failed to copy font name to create font.");
3632
3633 pFontInstance->hFont = ::CreateFontIndirectW(&lf);
3634 ThmExitOnNull(pFontInstance->hFont, hr, E_OUTOFMEMORY, "Failed to create DPI specific font.");
3635
3636 ++pFont->cFontInstances;
3637 *ppFontInstance = pFontInstance;
3638
3639LExit:
3640 return hr;
3641}
3642
3643
3644static HRESULT FindImageList(
3645 __in THEME* pTheme,
3646 __in_z LPCWSTR wzImageListName,
3647 __out HIMAGELIST *phImageList
3648 )
3649{
3650 HRESULT hr = S_OK;
3651
3652 for (DWORD i = 0; i < pTheme->cImageLists; ++i)
3653 {
3654 if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pTheme->rgImageLists[i].sczName, -1, wzImageListName, -1))
3655 {
3656 *phImageList = pTheme->rgImageLists[i].hImageList;
3657 ExitFunction1(hr = S_OK);
3658 }
3659 }
3660
3661 hr = E_NOTFOUND;
3662
3663LExit:
3664 return hr;
3665}
3666
3667
3668static HRESULT DrawButton(
3669 __in THEME* pTheme,
3670 __in DRAWITEMSTRUCT* pdis,
3671 __in const THEME_CONTROL* pControl
3672 )
3673{
3674 int nSourceX = pControl->hImage ? 0 : pControl->nSourceX;
3675 int nSourceY = pControl->hImage ? 0 : pControl->nSourceY;
3676 DWORD dwSourceWidth = pControl->nDefaultDpiWidth;
3677 DWORD dwSourceHeight = pControl->nDefaultDpiHeight;
3678
3679 HDC hdcMem = ::CreateCompatibleDC(pdis->hDC);
3680 HBITMAP hDefaultBitmap = static_cast<HBITMAP>(::SelectObject(hdcMem, pControl->hImage ? pControl->hImage : pTheme->hImage));
3681
3682 DWORD_PTR dwStyle = ::GetWindowLongPtrW(pdis->hwndItem, GWL_STYLE);
3683 // "clicked" gets priority
3684 if (ODS_SELECTED & pdis->itemState)
3685 {
3686 nSourceY += pControl->nDefaultDpiHeight * 2;
3687 }
3688 // then hover
3689 else if (pControl->dwData & THEME_CONTROL_DATA_HOVER)
3690 {
3691 nSourceY += pControl->nDefaultDpiHeight;
3692 }
3693 // then focused
3694 else if (WS_TABSTOP & dwStyle && ODS_FOCUS & pdis->itemState)
3695 {
3696 nSourceY += pControl->nDefaultDpiHeight * 3;
3697 }
3698
3699 ::StretchBlt(pdis->hDC, 0, 0, pControl->nWidth, pControl->nHeight, hdcMem, nSourceX, nSourceY, dwSourceWidth, dwSourceHeight, SRCCOPY);
3700
3701 ::SelectObject(hdcMem, hDefaultBitmap);
3702 ::DeleteDC(hdcMem);
3703
3704 DrawControlText(pTheme, pdis, pControl, TRUE, FALSE);
3705
3706 return S_OK;
3707}
3708
3709
3710static HRESULT DrawHyperlink(
3711 __in THEME* pTheme,
3712 __in DRAWITEMSTRUCT* pdis,
3713 __in const THEME_CONTROL* pControl
3714 )
3715{
3716 DrawControlText(pTheme, pdis, pControl, FALSE, TRUE);
3717 return S_OK;
3718}
3719
3720
3721static void DrawControlText(
3722 __in THEME* pTheme,
3723 __in DRAWITEMSTRUCT* pdis,
3724 __in const THEME_CONTROL* pControl,
3725 __in BOOL fCentered,
3726 __in BOOL fDrawFocusRect
3727 )
3728{
3729 HRESULT hr = S_OK;
3730 WCHAR wzText[256] = { };
3731 DWORD cchText = 0;
3732 THEME_FONT* pFont = NULL;
3733 THEME_FONT_INSTANCE* pFontInstance = NULL;
3734 HFONT hfPrev = NULL;
3735
3736 if (0 == (cchText = ::GetWindowTextW(pdis->hwndItem, wzText, countof(wzText))))
3737 {
3738 // nothing to do
3739 return;
3740 }
3741
3742 if (ODS_SELECTED & pdis->itemState)
3743 {
3744 pFont = pTheme->rgFonts + (THEME_INVALID_ID != pControl->dwFontSelectedId ? pControl->dwFontSelectedId : pControl->dwFontId);
3745 }
3746 else if (pControl->dwData & THEME_CONTROL_DATA_HOVER)
3747 {
3748 pFont = pTheme->rgFonts + (THEME_INVALID_ID != pControl->dwFontHoverId ? pControl->dwFontHoverId : pControl->dwFontId);
3749 }
3750 else
3751 {
3752 pFont = pTheme->rgFonts + pControl->dwFontId;
3753 }
3754
3755 hr = EnsureFontInstance(pTheme, pFont, &pFontInstance);
3756 if (SUCCEEDED(hr))
3757 {
3758 hfPrev = SelectFont(pdis->hDC, pFontInstance->hFont);
3759 }
3760
3761 ::DrawTextExW(pdis->hDC, wzText, cchText, &pdis->rcItem, DT_SINGLELINE | (fCentered ? (DT_CENTER | DT_VCENTER) : 0), NULL);
3762
3763 if (fDrawFocusRect && (WS_TABSTOP & ::GetWindowLongPtrW(pdis->hwndItem, GWL_STYLE)) && (ODS_FOCUS & pdis->itemState))
3764 {
3765 ::DrawFocusRect(pdis->hDC, &pdis->rcItem);
3766 }
3767
3768 if (hfPrev)
3769 {
3770 SelectFont(pdis->hDC, hfPrev);
3771 }
3772}
3773
3774
3775static HRESULT DrawImage(
3776 __in THEME* pTheme,
3777 __in DRAWITEMSTRUCT* pdis,
3778 __in const THEME_CONTROL* pControl
3779 )
3780{
3781 DWORD dwHeight = pdis->rcItem.bottom - pdis->rcItem.top;
3782 DWORD dwWidth = pdis->rcItem.right - pdis->rcItem.left;
3783 int nSourceX = pControl->hImage ? 0 : pControl->nSourceX;
3784 int nSourceY = pControl->hImage ? 0 : pControl->nSourceY;
3785 DWORD dwSourceHeight = pControl->nDefaultDpiHeight;
3786 DWORD dwSourceWidth = pControl->nDefaultDpiWidth;
3787
3788 BLENDFUNCTION bf = { };
3789 bf.BlendOp = AC_SRC_OVER;
3790 bf.SourceConstantAlpha = 255;
3791 bf.AlphaFormat = AC_SRC_ALPHA;
3792
3793 HDC hdcMem = ::CreateCompatibleDC(pdis->hDC);
3794 HBITMAP hDefaultBitmap = static_cast<HBITMAP>(::SelectObject(hdcMem, pControl->hImage ? pControl->hImage : pTheme->hImage));
3795
3796 // Try to draw the image with transparency and if that fails (usually because the image has no
3797 // alpha channel) then draw the image as is.
3798 if (!::AlphaBlend(pdis->hDC, 0, 0, dwWidth, dwHeight, hdcMem, nSourceX, nSourceY, dwSourceWidth, dwSourceHeight, bf))
3799 {
3800 ::StretchBlt(pdis->hDC, 0, 0, dwWidth, dwHeight, hdcMem, nSourceX, nSourceY, dwSourceWidth, dwSourceHeight, SRCCOPY);
3801 }
3802
3803 ::SelectObject(hdcMem, hDefaultBitmap);
3804 ::DeleteDC(hdcMem);
3805 return S_OK;
3806}
3807
3808
3809static HRESULT DrawProgressBar(
3810 __in THEME* pTheme,
3811 __in DRAWITEMSTRUCT* pdis,
3812 __in const THEME_CONTROL* pControl
3813 )
3814{
3815 DWORD dwProgressColor = HIWORD(pControl->dwData);
3816 DWORD dwProgressPercentage = LOWORD(pControl->dwData);
3817 DWORD dwHeight = pdis->rcItem.bottom - pdis->rcItem.top;
3818 DWORD dwCenter = (pdis->rcItem.right - 2) * dwProgressPercentage / 100;
3819 DWORD dwSourceHeight = pControl->nDefaultDpiHeight;
3820 int nSourceX = pControl->hImage ? 0 : pControl->nSourceX;
3821 int nSourceY = (pControl->hImage ? 0 : pControl->nSourceY) + (dwProgressColor * dwSourceHeight);
3822
3823 HDC hdcMem = ::CreateCompatibleDC(pdis->hDC);
3824 HBITMAP hDefaultBitmap = static_cast<HBITMAP>(::SelectObject(hdcMem, pControl->hImage ? pControl->hImage : pTheme->hImage));
3825
3826 // Draw the left side of the progress bar.
3827 ::StretchBlt(pdis->hDC, 0, 0, 1, dwHeight, hdcMem, nSourceX, nSourceY, 1, dwSourceHeight, SRCCOPY);
3828
3829 // Draw the filled side of the progress bar, if there is any.
3830 if (0 < dwCenter)
3831 {
3832 ::StretchBlt(pdis->hDC, 1, 0, dwCenter, dwHeight, hdcMem, nSourceX + 1, nSourceY, 1, dwSourceHeight, SRCCOPY);
3833 }
3834
3835 // Draw the unfilled side of the progress bar, if there is any.
3836 if (dwCenter < static_cast<DWORD>(pdis->rcItem.right - 2))
3837 {
3838 ::StretchBlt(pdis->hDC, 1 + dwCenter, 0, pdis->rcItem.right - dwCenter - 1, dwHeight, hdcMem, nSourceX + 2, nSourceY, 1, dwSourceHeight, SRCCOPY);
3839 }
3840
3841 // Draw the right side of the progress bar.
3842 ::StretchBlt(pdis->hDC, pdis->rcItem.right - 1, 0, 1, dwHeight, hdcMem, nSourceX + 3, nSourceY, 1, dwSourceHeight, SRCCOPY);
3843
3844 ::SelectObject(hdcMem, hDefaultBitmap);
3845 ::DeleteDC(hdcMem);
3846 return S_OK;
3847}
3848
3849
3850static BOOL DrawHoverControl(
3851 __in THEME* pTheme,
3852 __in BOOL fHover
3853 )
3854{
3855 BOOL fChangedHover = FALSE;
3856 THEME_CONTROL* pControl = const_cast<THEME_CONTROL*>(FindControlFromHWnd(pTheme, pTheme->hwndHover));
3857
3858 // Only hyperlinks and owner-drawn buttons have hover states.
3859 if (pControl && (THEME_CONTROL_TYPE_HYPERLINK == pControl->type ||
3860 (THEME_CONTROL_TYPE_BUTTON == pControl->type && (pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_OWNER_DRAW))))
3861 {
3862 if (fHover)
3863 {
3864 pControl->dwData |= THEME_CONTROL_DATA_HOVER;
3865 }
3866 else
3867 {
3868 pControl->dwData &= ~THEME_CONTROL_DATA_HOVER;
3869 }
3870
3871 ::InvalidateRect(pControl->hWnd, NULL, FALSE);
3872 fChangedHover = TRUE;
3873 }
3874
3875 return fChangedHover;
3876}
3877
3878
3879static void FreePage(
3880 __in THEME_PAGE* pPage
3881 )
3882{
3883 if (pPage)
3884 {
3885 ReleaseStr(pPage->sczName);
3886
3887 if (pPage->cSavedVariables)
3888 {
3889 for (DWORD i = 0; i < pPage->cSavedVariables; ++i)
3890 {
3891 ReleaseStr(pPage->rgSavedVariables[i].sczValue);
3892 }
3893 }
3894
3895 ReleaseMem(pPage->rgSavedVariables);
3896 }
3897}
3898
3899
3900static void FreeImageList(
3901 __in THEME_IMAGELIST* pImageList
3902 )
3903{
3904 if (pImageList)
3905 {
3906 ReleaseStr(pImageList->sczName);
3907 ImageList_Destroy(pImageList->hImageList);
3908 }
3909}
3910
3911static void FreeControl(
3912 __in THEME_CONTROL* pControl
3913 )
3914{
3915 if (pControl)
3916 {
3917 if (::IsWindow(pControl->hWnd))
3918 {
3919 ::CloseWindow(pControl->hWnd);
3920 pControl->hWnd = NULL;
3921 }
3922
3923 ReleaseStr(pControl->sczName);
3924 ReleaseStr(pControl->sczText);
3925 ReleaseStr(pControl->sczTooltip);
3926 ReleaseStr(pControl->sczNote);
3927 ReleaseStr(pControl->sczEnableCondition);
3928 ReleaseStr(pControl->sczVisibleCondition);
3929 ReleaseStr(pControl->sczValue);
3930 ReleaseStr(pControl->sczVariable);
3931
3932 if (pControl->hImage)
3933 {
3934 ::DeleteBitmap(pControl->hImage);
3935 }
3936
3937 for (DWORD i = 0; i < pControl->cControls; ++i)
3938 {
3939 FreeControl(pControl->rgControls + i);
3940 }
3941
3942 for (DWORD i = 0; i < pControl->cActions; ++i)
3943 {
3944 FreeAction(&(pControl->rgActions[i]));
3945 }
3946
3947 for (DWORD i = 0; i < pControl->cColumns; ++i)
3948 {
3949 FreeColumn(&(pControl->ptcColumns[i]));
3950 }
3951
3952 for (DWORD i = 0; i < pControl->cConditionalText; ++i)
3953 {
3954 FreeConditionalText(&(pControl->rgConditionalText[i]));
3955 }
3956
3957 for (DWORD i = 0; i < pControl->cConditionalNotes; ++i)
3958 {
3959 FreeConditionalText(&(pControl->rgConditionalNotes[i]));
3960 }
3961
3962 for (DWORD i = 0; i < pControl->cTabs; ++i)
3963 {
3964 FreeTab(&(pControl->pttTabs[i]));
3965 }
3966
3967 ReleaseMem(pControl->rgActions)
3968 ReleaseMem(pControl->ptcColumns);
3969 ReleaseMem(pControl->rgConditionalText);
3970 ReleaseMem(pControl->rgConditionalNotes);
3971 ReleaseMem(pControl->pttTabs);
3972 }
3973}
3974
3975
3976static void FreeAction(
3977 __in THEME_ACTION* pAction
3978 )
3979{
3980 switch (pAction->type)
3981 {
3982 case THEME_ACTION_TYPE_BROWSE_DIRECTORY:
3983 ReleaseStr(pAction->BrowseDirectory.sczVariableName);
3984 break;
3985 case THEME_ACTION_TYPE_CHANGE_PAGE:
3986 ReleaseStr(pAction->ChangePage.sczPageName);
3987 break;
3988 }
3989
3990 ReleaseStr(pAction->sczCondition);
3991}
3992
3993
3994static void FreeColumn(
3995 __in THEME_COLUMN* pColumn
3996 )
3997{
3998 ReleaseStr(pColumn->pszName);
3999}
4000
4001
4002static void FreeConditionalText(
4003 __in THEME_CONDITIONAL_TEXT* pConditionalText
4004 )
4005{
4006 ReleaseStr(pConditionalText->sczCondition);
4007 ReleaseStr(pConditionalText->sczText);
4008}
4009
4010
4011static void FreeTab(
4012 __in THEME_TAB* pTab
4013 )
4014{
4015 ReleaseStr(pTab->pszName);
4016}
4017
4018
4019static void FreeFontInstance(
4020 __in THEME_FONT_INSTANCE* pFontInstance
4021 )
4022{
4023 if (pFontInstance->hFont)
4024 {
4025 ::DeleteObject(pFontInstance->hFont);
4026 pFontInstance->hFont = NULL;
4027 }
4028}
4029
4030
4031static void FreeFont(
4032 __in THEME_FONT* pFont
4033 )
4034{
4035 if (pFont)
4036 {
4037 if (pFont->hBackground)
4038 {
4039 ::DeleteObject(pFont->hBackground);
4040 pFont->hBackground = NULL;
4041 }
4042
4043 if (pFont->hForeground)
4044 {
4045 ::DeleteObject(pFont->hForeground);
4046 pFont->hForeground = NULL;
4047 }
4048
4049 for (DWORD i = 0; i < pFont->cFontInstances; ++i)
4050 {
4051 FreeFontInstance(&(pFont->rgFontInstances[i]));
4052 }
4053
4054 ReleaseMem(pFont->rgFontInstances);
4055 ReleaseStr(pFont->sczFaceName);
4056 }
4057}
4058
4059
4060static DWORD CALLBACK RichEditStreamFromFileHandleCallback(
4061 __in DWORD_PTR dwCookie,
4062 __in_bcount(cb) LPBYTE pbBuff,
4063 __in LONG cb,
4064 __in LONG* pcb
4065 )
4066{
4067 HRESULT hr = S_OK;
4068 HANDLE hFile = reinterpret_cast<HANDLE>(dwCookie);
4069
4070 if (!::ReadFile(hFile, pbBuff, cb, reinterpret_cast<DWORD*>(pcb), NULL))
4071 {
4072 ThmExitWithLastError(hr, "Failed to read file");
4073 }
4074
4075LExit:
4076 return hr;
4077}
4078
4079
4080static DWORD CALLBACK RichEditStreamFromMemoryCallback(
4081 __in DWORD_PTR dwCookie,
4082 __in_bcount(cb) LPBYTE pbBuff,
4083 __in LONG cb,
4084 __in LONG* pcb
4085 )
4086{
4087 HRESULT hr = S_OK;
4088 MEMBUFFER_FOR_RICHEDIT* pBuffer = reinterpret_cast<MEMBUFFER_FOR_RICHEDIT*>(dwCookie);
4089 DWORD cbCopy = 0;
4090
4091 if (pBuffer->iData < pBuffer->cbData)
4092 {
4093 cbCopy = min(static_cast<DWORD>(cb), pBuffer->cbData - pBuffer->iData);
4094 memcpy(pbBuff, pBuffer->rgbData + pBuffer->iData, cbCopy);
4095
4096 pBuffer->iData += cbCopy;
4097 Assert(pBuffer->iData <= pBuffer->cbData);
4098 }
4099
4100 *pcb = cbCopy;
4101 return hr;
4102}
4103
4104
4105static void CALLBACK OnBillboardTimer(
4106 __in THEME* pTheme,
4107 __in HWND hwnd,
4108 __in UINT_PTR idEvent
4109 )
4110{
4111 HWND hwndControl = ::GetDlgItem(hwnd, static_cast<int>(idEvent));
4112 if (hwndControl)
4113 {
4114 THEME_CONTROL* pControl = const_cast<THEME_CONTROL*>(FindControlFromHWnd(pTheme, hwndControl));
4115 AssertSz(pControl && THEME_CONTROL_TYPE_BILLBOARD == pControl->type, "Only billboard controls should get billboard timer messages.");
4116
4117 if (pControl)
4118 {
4119 if (pControl->dwData < pControl->cControls)
4120 {
4121 ThemeShowChild(pTheme, pControl, pControl->dwData);
4122 }
4123 else if (pControl->fBillboardLoops)
4124 {
4125 pControl->dwData = 0;
4126 ThemeShowChild(pTheme, pControl, pControl->dwData);
4127 }
4128 else // no more looping
4129 {
4130 ::KillTimer(hwnd, idEvent);
4131 }
4132
4133 ++pControl->dwData;
4134 }
4135 }
4136}
4137
4138static void OnBrowseDirectory(
4139 __in THEME* pTheme,
4140 __in HWND hWnd,
4141 __in const THEME_ACTION* pAction
4142 )
4143{
4144 HRESULT hr = S_OK;
4145 WCHAR wzPath[MAX_PATH] = { };
4146 BROWSEINFOW browseInfo = { };
4147 PIDLIST_ABSOLUTE pidl = NULL;
4148
4149 browseInfo.hwndOwner = hWnd;
4150 browseInfo.pszDisplayName = wzPath;
4151 browseInfo.lpszTitle = pTheme->sczCaption;
4152 browseInfo.ulFlags = BIF_RETURNONLYFSDIRS | BIF_USENEWUI;
4153 pidl = ::SHBrowseForFolderW(&browseInfo);
4154 if (pidl && ::SHGetPathFromIDListW(pidl, wzPath))
4155 {
4156 // Since editbox changes aren't immediately saved off, we have to treat them differently.
4157 THEME_CONTROL* pTargetControl = NULL;
4158
4159 for (DWORD i = 0; i < pTheme->cControls; ++i)
4160 {
4161 THEME_CONTROL* pControl = pTheme->rgControls + i;
4162
4163 if ((!pControl->wPageId || pControl->wPageId == pTheme->dwCurrentPageId) &&
4164 CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pControl->sczName, -1, pAction->BrowseDirectory.sczVariableName, -1))
4165 {
4166 pTargetControl = pControl;
4167 break;
4168 }
4169 }
4170
4171 if (pTargetControl && THEME_CONTROL_TYPE_EDITBOX == pTargetControl->type && !pTargetControl->fDisableVariableFunctionality)
4172 {
4173 hr = ThemeSetTextControl(pTheme, pTargetControl->wId, wzPath);
4174 ThmExitOnFailure(hr, "Failed to set text on editbox: %ls", pTargetControl->sczName);
4175 }
4176 else if (pTheme->pfnSetStringVariable)
4177 {
4178 hr = pTheme->pfnSetStringVariable(pAction->BrowseDirectory.sczVariableName, wzPath, FALSE, pTheme->pvVariableContext);
4179 ThmExitOnFailure(hr, "Failed to set variable: %ls", pAction->BrowseDirectory.sczVariableName);
4180 }
4181 else if (pTargetControl)
4182 {
4183 hr = ThemeSetTextControl(pTheme, pTargetControl->wId, wzPath);
4184 ThmExitOnFailure(hr, "Failed to set text on control: %ls", pTargetControl->sczName);
4185 }
4186
4187 ThemeShowPageEx(pTheme, pTheme->dwCurrentPageId, SW_SHOW, THEME_SHOW_PAGE_REASON_REFRESH);
4188 }
4189
4190LExit:
4191 if (pidl)
4192 {
4193 ::CoTaskMemFree(pidl);
4194 }
4195}
4196
4197static BOOL OnButtonClicked(
4198 __in THEME* pTheme,
4199 __in HWND hWnd,
4200 __in const THEME_CONTROL* pControl
4201 )
4202{
4203 HRESULT hr = S_OK;
4204 BOOL fHandled = FALSE;
4205
4206 if (THEME_CONTROL_TYPE_BUTTON == pControl->type || THEME_CONTROL_TYPE_COMMANDLINK == pControl->type)
4207 {
4208 if (pControl->cActions)
4209 {
4210 fHandled = TRUE;
4211 THEME_ACTION* pChosenAction = pControl->pDefaultAction;
4212
4213 if (pTheme->pfnEvaluateCondition)
4214 {
4215 // As documented in the xsd, if there are multiple conditions that are true at the same time then the behavior is undefined.
4216 // This is the current implementation and can change at any time.
4217 for (DWORD j = 0; j < pControl->cActions; ++j)
4218 {
4219 THEME_ACTION* pAction = pControl->rgActions + j;
4220
4221 if (pAction->sczCondition)
4222 {
4223 BOOL fCondition = FALSE;
4224
4225 hr = pTheme->pfnEvaluateCondition(pAction->sczCondition, &fCondition, pTheme->pvVariableContext);
4226 ThmExitOnFailure(hr, "Failed to evaluate condition: %ls", pAction->sczCondition);
4227
4228 if (fCondition)
4229 {
4230 pChosenAction = pAction;
4231 break;
4232 }
4233 }
4234 }
4235 }
4236
4237 if (pChosenAction)
4238 {
4239 switch (pChosenAction->type)
4240 {
4241 case THEME_ACTION_TYPE_BROWSE_DIRECTORY:
4242 OnBrowseDirectory(pTheme, hWnd, pChosenAction);
4243 break;
4244
4245 case THEME_ACTION_TYPE_CLOSE_WINDOW:
4246 ::SendMessageW(hWnd, WM_CLOSE, 0, 0);
4247 break;
4248
4249 case THEME_ACTION_TYPE_CHANGE_PAGE:
4250 DWORD dwPageId = 0;
4251 LPCWSTR pPageNames = pChosenAction->ChangePage.sczPageName;
4252 ThemeGetPageIds(pTheme, &pPageNames, &dwPageId, 1);
4253
4254 if (!dwPageId)
4255 {
4256 ThmExitOnFailure(E_INVALIDDATA, "Unknown page: %ls", pChosenAction->ChangePage.sczPageName);
4257 }
4258
4259 ThemeShowPageEx(pTheme, pTheme->dwCurrentPageId, SW_HIDE, pChosenAction->ChangePage.fCancel ? THEME_SHOW_PAGE_REASON_CANCEL : THEME_SHOW_PAGE_REASON_DEFAULT);
4260 ThemeShowPage(pTheme, dwPageId, SW_SHOW);
4261 break;
4262 }
4263 }
4264 }
4265 }
4266 else if (!pControl->fDisableVariableFunctionality && (pTheme->pfnSetNumericVariable || pTheme->pfnSetStringVariable))
4267 {
4268 BOOL fRefresh = FALSE;
4269
4270 switch (pControl->type)
4271 {
4272 case THEME_CONTROL_TYPE_CHECKBOX:
4273 if (pTheme->pfnSetNumericVariable && pControl->sczName && *pControl->sczName)
4274 {
4275 BOOL fChecked = ThemeIsControlChecked(pTheme, pControl->wId);
4276 pTheme->pfnSetNumericVariable(pControl->sczName, fChecked ? 1 : 0, pTheme->pvVariableContext);
4277 fRefresh = TRUE;
4278 }
4279 break;
4280 case THEME_CONTROL_TYPE_RADIOBUTTON:
4281 if (pTheme->pfnSetStringVariable && pControl->sczVariable && *pControl->sczVariable && ThemeIsControlChecked(pTheme, pControl->wId))
4282 {
4283 pTheme->pfnSetStringVariable(pControl->sczVariable, pControl->sczValue, FALSE, pTheme->pvVariableContext);
4284 fRefresh = TRUE;
4285 }
4286 break;
4287 }
4288
4289 if (fRefresh)
4290 {
4291 ThemeShowPageEx(pTheme, pTheme->dwCurrentPageId, SW_SHOW, THEME_SHOW_PAGE_REASON_REFRESH);
4292 fHandled = TRUE;
4293 }
4294 }
4295
4296LExit:
4297 return fHandled;
4298}
4299
4300static BOOL OnDpiChanged(
4301 __in THEME* pTheme,
4302 __in WPARAM wParam,
4303 __in LPARAM lParam
4304 )
4305{
4306 UINT nDpi = HIWORD(wParam);
4307 RECT* pRect = reinterpret_cast<RECT*>(lParam);
4308 BOOL fIgnored = pTheme->nDpi == nDpi;
4309
4310 if (fIgnored)
4311 {
4312 ExitFunction();
4313 }
4314
4315
4316 pTheme->fForceResize = !pTheme->fAutoResize;
4317 ScaleThemeFromWindow(pTheme, nDpi, pRect->left, pRect->top);
4318
4319LExit:
4320 return !fIgnored;
4321}
4322
4323static void OnNcCreate(
4324 __in THEME* pTheme,
4325 __in HWND hWnd,
4326 __in LPARAM lParam
4327 )
4328{
4329 DPIU_WINDOW_CONTEXT windowContext = { };
4330 CREATESTRUCTW* pCreateStruct = reinterpret_cast<CREATESTRUCTW*>(lParam);
4331
4332 pTheme->hwndParent = hWnd;
4333
4334 DpiuGetWindowContext(pTheme->hwndParent, &windowContext);
4335
4336 if (windowContext.nDpi != pTheme->nDpi)
4337 {
4338 ScaleTheme(pTheme, windowContext.nDpi, pCreateStruct->x, pCreateStruct->y, pCreateStruct->style, NULL != pCreateStruct->hMenu, pCreateStruct->dwExStyle);
4339 }
4340}
4341
4342static HRESULT OnRichEditEnLink(
4343 __in LPARAM lParam,
4344 __in HWND hWndRichEdit,
4345 __in HWND hWnd
4346 )
4347{
4348 HRESULT hr = S_OK;
4349 LPWSTR sczLink = NULL;
4350 ENLINK* link = reinterpret_cast<ENLINK*>(lParam);
4351
4352 switch (link->msg)
4353 {
4354 case WM_LBUTTONDOWN:
4355 {
4356 hr = StrAlloc(&sczLink, link->chrg.cpMax - link->chrg.cpMin + 2);
4357 ThmExitOnFailure(hr, "Failed to allocate string for link.");
4358
4359 TEXTRANGEW tr;
4360 tr.chrg.cpMin = link->chrg.cpMin;
4361 tr.chrg.cpMax = link->chrg.cpMax;
4362 tr.lpstrText = sczLink;
4363
4364 if (0 < ::SendMessageW(hWndRichEdit, EM_GETTEXTRANGE, 0, reinterpret_cast<LPARAM>(&tr)))
4365 {
4366 hr = ShelExec(sczLink, NULL, L"open", NULL, SW_SHOWDEFAULT, hWnd, NULL);
4367 ThmExitOnFailure(hr, "Failed to launch link: %ls", sczLink);
4368 }
4369
4370 break;
4371 }
4372
4373 case WM_SETCURSOR:
4374 ::SetCursor(vhCursorHand);
4375 break;
4376 }
4377
4378LExit:
4379 ReleaseStr(sczLink);
4380
4381 return hr;
4382}
4383
4384static BOOL ControlIsType(
4385 __in const THEME* pTheme,
4386 __in DWORD dwControl,
4387 __in const THEME_CONTROL_TYPE type
4388 )
4389{
4390 BOOL fIsType = FALSE;
4391 HWND hWnd = ::GetDlgItem(pTheme->hwndParent, dwControl);
4392 if (hWnd)
4393 {
4394 const THEME_CONTROL* pControl = FindControlFromHWnd(pTheme, hWnd);
4395 fIsType = (pControl && type == pControl->type);
4396 }
4397
4398 return fIsType;
4399}
4400
4401static const THEME_CONTROL* FindControlFromHWnd(
4402 __in const THEME* pTheme,
4403 __in HWND hWnd,
4404 __in_opt const THEME_CONTROL* pParentControl
4405 )
4406{
4407 DWORD cControls = 0;
4408 THEME_CONTROL* rgControls = NULL;
4409
4410 GetControls(pTheme, pParentControl, cControls, rgControls);
4411
4412 // As we can't use GWLP_USERDATA (SysLink controls on Windows XP uses it too)...
4413 for (DWORD i = 0; i < cControls; ++i)
4414 {
4415 if (hWnd == rgControls[i].hWnd)
4416 {
4417 return rgControls + i;
4418 }
4419 else if (0 < rgControls[i].cControls)
4420 {
4421 const THEME_CONTROL* pChildControl = FindControlFromHWnd(pTheme, hWnd, rgControls + i);
4422 if (pChildControl)
4423 {
4424 return pChildControl;
4425 }
4426 }
4427 }
4428
4429 return NULL;
4430}
4431
4432static void GetControlDimensions(
4433 __in const THEME_CONTROL* pControl,
4434 __in const RECT* prcParent,
4435 __out int* piWidth,
4436 __out int* piHeight,
4437 __out int* piX,
4438 __out int* piY
4439 )
4440{
4441 *piWidth = pControl->nWidth + (0 < pControl->nWidth ? 0 : prcParent->right - max(0, pControl->nX));
4442 *piHeight = pControl->nHeight + (0 < pControl->nHeight ? 0 : prcParent->bottom - max(0, pControl->nY));
4443 *piX = pControl->nX + (-1 < pControl->nX ? 0 : prcParent->right - *piWidth);
4444 *piY = pControl->nY + (-1 < pControl->nY ? 0 : prcParent->bottom - *piHeight);
4445}
4446
4447static HRESULT SizeListViewColumns(
4448 __inout THEME_CONTROL* pControl
4449 )
4450{
4451 HRESULT hr = S_OK;
4452 RECT rcParent = { };
4453 int cNumExpandingColumns = 0;
4454 int iExtraAvailableSize;
4455
4456 if (!::GetWindowRect(pControl->hWnd, &rcParent))
4457 {
4458 ThmExitWithLastError(hr, "Failed to get window rect of listview control.");
4459 }
4460
4461 iExtraAvailableSize = rcParent.right - rcParent.left;
4462
4463 for (DWORD i = 0; i < pControl->cColumns; ++i)
4464 {
4465 if (pControl->ptcColumns[i].fExpands)
4466 {
4467 ++cNumExpandingColumns;
4468 }
4469
4470 iExtraAvailableSize -= pControl->ptcColumns[i].nBaseWidth;
4471 }
4472
4473 // Leave room for a vertical scroll bar just in case.
4474 iExtraAvailableSize -= ::GetSystemMetrics(SM_CXVSCROLL);
4475
4476 for (DWORD i = 0; i < pControl->cColumns; ++i)
4477 {
4478 if (pControl->ptcColumns[i].fExpands)
4479 {
4480 pControl->ptcColumns[i].nWidth = pControl->ptcColumns[i].nBaseWidth + (iExtraAvailableSize / cNumExpandingColumns);
4481 // In case there is any remainder, use it up the first chance we get.
4482 pControl->ptcColumns[i].nWidth += iExtraAvailableSize % cNumExpandingColumns;
4483 iExtraAvailableSize -= iExtraAvailableSize % cNumExpandingColumns;
4484 }
4485 else
4486 {
4487 pControl->ptcColumns[i].nWidth = pControl->ptcColumns[i].nBaseWidth;
4488 }
4489 }
4490
4491LExit:
4492 return hr;
4493}
4494
4495
4496static HRESULT ShowControl(
4497 __in THEME* pTheme,
4498 __in THEME_CONTROL* pControl,
4499 __in int nCmdShow,
4500 __in BOOL fSaveEditboxes,
4501 __in THEME_SHOW_PAGE_REASON reason,
4502 __in DWORD dwPageId,
4503 __out_opt HWND* phwndFocus
4504 )
4505{
4506 HRESULT hr = S_OK;
4507 DWORD iPageControl = 0;
4508 HWND hwndFocus = NULL;
4509 LPWSTR sczFormatString = NULL;
4510 LPWSTR sczText = NULL;
4511 THEME_SAVEDVARIABLE* pSavedVariable = NULL;
4512 BOOL fHide = SW_HIDE == nCmdShow;
4513 THEME_PAGE* pPage = ThemeGetPage(pTheme, dwPageId);
4514
4515 // Save the editbox value if necessary (other control types save their values immediately).
4516 if (pTheme->pfnSetStringVariable && !pControl->fDisableVariableFunctionality &&
4517 fSaveEditboxes && THEME_CONTROL_TYPE_EDITBOX == pControl->type && pControl->sczName && *pControl->sczName)
4518 {
4519 hr = ThemeGetTextControl(pTheme, pControl->wId, &sczText);
4520 ThmExitOnFailure(hr, "Failed to get the text for control: %ls", pControl->sczName);
4521
4522 hr = pTheme->pfnSetStringVariable(pControl->sczName, sczText, FALSE, pTheme->pvVariableContext);
4523 ThmExitOnFailure(hr, "Failed to set the variable '%ls' to '%ls'", pControl->sczName, sczText);
4524 }
4525
4526 HWND hWnd = pControl->hWnd;
4527
4528 if (fHide && pControl->wPageId)
4529 {
4530 ::ShowWindow(hWnd, SW_HIDE);
4531
4532 if (THEME_CONTROL_TYPE_BILLBOARD == pControl->type)
4533 {
4534 StopBillboard(pTheme, pControl->wId);
4535 }
4536
4537 ExitFunction();
4538 }
4539
4540 BOOL fEnabled = !(pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_DISABLED);
4541 BOOL fVisible = !(pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_HIDDEN);
4542
4543 if (!pControl->fDisableVariableFunctionality)
4544 {
4545 if (pTheme->pfnEvaluateCondition)
4546 {
4547 // If the control has a VisibleCondition, check if it's true.
4548 if (pControl->sczVisibleCondition)
4549 {
4550 hr = pTheme->pfnEvaluateCondition(pControl->sczVisibleCondition, &fVisible, pTheme->pvVariableContext);
4551 ThmExitOnFailure(hr, "Failed to evaluate VisibleCondition: %ls", pControl->sczVisibleCondition);
4552 }
4553
4554 // If the control has an EnableCondition, check if it's true.
4555 if (pControl->sczEnableCondition)
4556 {
4557 hr = pTheme->pfnEvaluateCondition(pControl->sczEnableCondition, &fEnabled, pTheme->pvVariableContext);
4558 ThmExitOnFailure(hr, "Failed to evaluate EnableCondition: %ls", pControl->sczEnableCondition);
4559 }
4560 }
4561
4562 // Try to format each control's text based on context, except for editboxes since their text comes from the user.
4563 if (pTheme->pfnFormatString && ((pControl->sczText && *pControl->sczText) || pControl->cConditionalText) && THEME_CONTROL_TYPE_EDITBOX != pControl->type)
4564 {
4565 LPWSTR wzText = pControl->sczText;
4566 LPWSTR wzNote = pControl->sczNote;
4567
4568 if (pTheme->pfnEvaluateCondition)
4569 {
4570 // As documented in the xsd, if there are multiple conditions that are true at the same time then the behavior is undefined.
4571 // This is the current implementation and can change at any time.
4572 for (DWORD j = 0; j < pControl->cConditionalText; ++j)
4573 {
4574 THEME_CONDITIONAL_TEXT* pConditionalText = pControl->rgConditionalText + j;
4575 wzText = pConditionalText->sczText;
4576
4577 if (pConditionalText->sczCondition)
4578 {
4579 BOOL fCondition = FALSE;
4580
4581 hr = pTheme->pfnEvaluateCondition(pConditionalText->sczCondition, &fCondition, pTheme->pvVariableContext);
4582 ThmExitOnFailure(hr, "Failed to evaluate condition: %ls", pConditionalText->sczCondition);
4583
4584 if (fCondition)
4585 {
4586 wzText = pConditionalText->sczText;
4587 break;
4588 }
4589 }
4590 }
4591
4592 for (DWORD j = 0; j < pControl->cConditionalNotes; ++j)
4593 {
4594 THEME_CONDITIONAL_TEXT* pConditionalNote = pControl->rgConditionalNotes + j;
4595 wzNote = pConditionalNote->sczText;
4596
4597 if (pConditionalNote->sczCondition)
4598 {
4599 BOOL fCondition = FALSE;
4600
4601 hr = pTheme->pfnEvaluateCondition(pConditionalNote->sczCondition, &fCondition, pTheme->pvVariableContext);
4602 ThmExitOnFailure(hr, "Failed to evaluate note condition: %ls", pConditionalNote->sczCondition);
4603
4604 if (fCondition)
4605 {
4606 wzNote = pConditionalNote->sczText;
4607 break;
4608 }
4609 }
4610 }
4611 }
4612
4613 if (wzText && *wzText)
4614 {
4615 hr = pTheme->pfnFormatString(wzText, &sczText, pTheme->pvVariableContext);
4616 ThmExitOnFailure(hr, "Failed to format string: %ls", wzText);
4617 }
4618 else
4619 {
4620 ReleaseNullStr(sczText);
4621 }
4622
4623 ThemeSetTextControl(pTheme, pControl->wId, sczText);
4624
4625 if (wzNote && *wzNote)
4626 {
4627 hr = pTheme->pfnFormatString(wzNote, &sczText, pTheme->pvVariableContext);
4628 ThmExitOnFailure(hr, "Failed to format note: %ls", wzNote);
4629 }
4630 else
4631 {
4632 ReleaseNullStr(sczText);
4633 }
4634
4635 ::SendMessageW(pControl->hWnd, BCM_SETNOTE, 0, reinterpret_cast<WPARAM>(sczText));
4636 }
4637
4638 // If this is a named control, do variable magic.
4639 if (pControl->sczName && *pControl->sczName)
4640 {
4641 // If this is a checkbox control,
4642 // try to set its default state to the state of a matching named variable.
4643 if (pTheme->pfnGetNumericVariable && THEME_CONTROL_TYPE_CHECKBOX == pControl->type)
4644 {
4645 LONGLONG llValue = 0;
4646 hr = pTheme->pfnGetNumericVariable(pControl->sczName, &llValue, pTheme->pvVariableContext);
4647 if (E_NOTFOUND == hr)
4648 {
4649 hr = S_OK;
4650 }
4651 ThmExitOnFailure(hr, "Failed to get numeric variable: %ls", pControl->sczName);
4652
4653 if (THEME_SHOW_PAGE_REASON_REFRESH != reason && pPage && pControl->wPageId)
4654 {
4655 pSavedVariable = pPage->rgSavedVariables + iPageControl;
4656 pSavedVariable->wzName = pControl->sczName;
4657
4658 if (SUCCEEDED(hr))
4659 {
4660 hr = StrAllocFormattedSecure(&pSavedVariable->sczValue, L"%lld", llValue);
4661 ThmExitOnFailure(hr, "Failed to save variable: %ls", pControl->sczName);
4662 }
4663
4664 ++iPageControl;
4665 }
4666
4667 ThemeSendControlMessage(pTheme, pControl->wId, BM_SETCHECK, SUCCEEDED(hr) && llValue ? BST_CHECKED : BST_UNCHECKED, 0);
4668 }
4669
4670 // If this is an editbox control,
4671 // try to set its default state to the state of a matching named variable.
4672 if (pTheme->pfnFormatString && THEME_CONTROL_TYPE_EDITBOX == pControl->type)
4673 {
4674 hr = StrAllocFormatted(&sczFormatString, L"[%ls]", pControl->sczName);
4675 ThmExitOnFailure(hr, "Failed to create format string: '%ls'", pControl->sczName);
4676
4677 hr = pTheme->pfnFormatString(sczFormatString, &sczText, pTheme->pvVariableContext);
4678 ThmExitOnFailure(hr, "Failed to format string: '%ls'", sczFormatString);
4679
4680 if (THEME_SHOW_PAGE_REASON_REFRESH != reason && pPage && pControl->wPageId)
4681 {
4682 pSavedVariable = pPage->rgSavedVariables + iPageControl;
4683 pSavedVariable->wzName = pControl->sczName;
4684
4685 if (SUCCEEDED(hr))
4686 {
4687 hr = StrAllocStringSecure(&pSavedVariable->sczValue, sczText, 0);
4688 ThmExitOnFailure(hr, "Failed to save variable: %ls", pControl->sczName);
4689 }
4690
4691 ++iPageControl;
4692 }
4693
4694 ThemeSetTextControl(pTheme, pControl->wId, sczText);
4695 }
4696 }
4697
4698 // If this is a radio button associated with a variable,
4699 // try to set its default state to the state of the variable.
4700 if (pTheme->pfnGetStringVariable && THEME_CONTROL_TYPE_RADIOBUTTON == pControl->type && pControl->sczVariable && *pControl->sczVariable)
4701 {
4702 hr = pTheme->pfnGetStringVariable(pControl->sczVariable, &sczText, pTheme->pvVariableContext);
4703 if (E_NOTFOUND == hr)
4704 {
4705 ReleaseNullStr(sczText);
4706 }
4707 else
4708 {
4709 ThmExitOnFailure(hr, "Failed to get string variable: %ls", pControl->sczVariable);
4710 }
4711
4712 if (THEME_SHOW_PAGE_REASON_REFRESH != reason && pPage && pControl->wPageId && pControl->fLastRadioButton)
4713 {
4714 pSavedVariable = pPage->rgSavedVariables + iPageControl;
4715 pSavedVariable->wzName = pControl->sczVariable;
4716
4717 if (SUCCEEDED(hr))
4718 {
4719 hr = StrAllocStringSecure(&pSavedVariable->sczValue, sczText, 0);
4720 ThmExitOnFailure(hr, "Failed to save variable: %ls", pControl->sczVariable);
4721 }
4722
4723 ++iPageControl;
4724 }
4725
4726 hr = S_OK;
4727
4728 Button_SetCheck(hWnd, (!sczText && !pControl->sczValue) || (sczText && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczText, -1, pControl->sczValue, -1)));
4729 }
4730 }
4731
4732 if (!fVisible || (!fEnabled && (pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_HIDE_WHEN_DISABLED)))
4733 {
4734 ::ShowWindow(hWnd, SW_HIDE);
4735 }
4736 else
4737 {
4738 ::EnableWindow(hWnd, !fHide && fEnabled);
4739
4740 if (!hwndFocus && pControl->wPageId && (pControl->dwStyle & WS_TABSTOP))
4741 {
4742 hwndFocus = hWnd;
4743 }
4744
4745 ::ShowWindow(hWnd, nCmdShow);
4746 }
4747
4748 if (0 < pControl->cControls)
4749 {
4750 ShowControls(pTheme, pControl, nCmdShow, fSaveEditboxes, reason, dwPageId);
4751 }
4752
4753 if (THEME_CONTROL_TYPE_BILLBOARD == pControl->type && pControl->wPageId)
4754 {
4755 if (fEnabled)
4756 {
4757 StartBillboard(pTheme, pControl->wId);
4758 }
4759 else
4760 {
4761 StopBillboard(pTheme, pControl->wId);
4762 }
4763 }
4764
4765 if (phwndFocus)
4766 {
4767 *phwndFocus = hwndFocus;
4768 }
4769
4770LExit:
4771 ReleaseStr(sczFormatString);
4772 ReleaseStr(sczText);
4773
4774 return hr;
4775}
4776
4777static HRESULT ShowControls(
4778 __in THEME* pTheme,
4779 __in_opt const THEME_CONTROL* pParentControl,
4780 __in int nCmdShow,
4781 __in BOOL fSaveEditboxes,
4782 __in THEME_SHOW_PAGE_REASON reason,
4783 __in DWORD dwPageId
4784 )
4785{
4786 HRESULT hr = S_OK;
4787 HWND hwndFocus = NULL;
4788 DWORD cControls = 0;
4789 THEME_CONTROL* rgControls = NULL;
4790
4791 GetControls(pTheme, pParentControl, cControls, rgControls);
4792
4793 for (DWORD i = 0; i < cControls; ++i)
4794 {
4795 THEME_CONTROL* pControl = rgControls + i;
4796
4797 // Only look at non-page controls and the specified page's controls.
4798 if (!pControl->wPageId || pControl->wPageId == dwPageId)
4799 {
4800 hr = ShowControl(pTheme, pControl, nCmdShow, fSaveEditboxes, reason, dwPageId, &hwndFocus);
4801 ThmExitOnFailure(hr, "Failed to show control '%ls' at index %d.", pControl->sczName, i);
4802 }
4803 }
4804
4805 if (hwndFocus)
4806 {
4807 ::SetFocus(hwndFocus);
4808 }
4809
4810LExit:
4811 return hr;
4812}
4813
4814
4815static LRESULT CALLBACK ControlGroupDefWindowProc(
4816 __in_opt THEME* pTheme,
4817 __in HWND hWnd,
4818 __in UINT uMsg,
4819 __in WPARAM wParam,
4820 __in LPARAM lParam
4821 )
4822{
4823 if (pTheme)
4824 {
4825 switch (uMsg)
4826 {
4827 case WM_DRAWITEM:
4828 ThemeDrawControl(pTheme, reinterpret_cast<LPDRAWITEMSTRUCT>(lParam));
4829 return TRUE;
4830
4831 case WM_CTLCOLORBTN: __fallthrough;
4832 case WM_CTLCOLORSTATIC:
4833 {
4834 HBRUSH hBrush = NULL;
4835 if (ThemeSetControlColor(pTheme, reinterpret_cast<HDC>(wParam), reinterpret_cast<HWND>(lParam), &hBrush))
4836 {
4837 return reinterpret_cast<LRESULT>(hBrush);
4838 }
4839 }
4840 break;
4841
4842 case WM_SETCURSOR:
4843 if (ThemeHoverControl(pTheme, hWnd, reinterpret_cast<HWND>(wParam)))
4844 {
4845 return TRUE;
4846 }
4847 break;
4848
4849 case WM_PAINT:
4850 if (::GetUpdateRect(hWnd, NULL, FALSE))
4851 {
4852 PAINTSTRUCT ps;
4853 ::BeginPaint(hWnd, &ps);
4854 if (hWnd == pTheme->hwndParent)
4855 {
4856 ThemeDrawBackground(pTheme, &ps);
4857 }
4858 ::EndPaint(hWnd, &ps);
4859 }
4860 return 0;
4861
4862 case WM_TIMER:
4863 OnBillboardTimer(pTheme, hWnd, wParam);
4864 break;
4865
4866 case WM_NOTIFY:
4867 if (lParam)
4868 {
4869 LPNMHDR pnmhdr = reinterpret_cast<LPNMHDR>(lParam);
4870 switch (pnmhdr->code)
4871 {
4872 // Tab/Shift+Tab support for rich-edit control.
4873 case EN_MSGFILTER:
4874 {
4875 MSGFILTER* msgFilter = reinterpret_cast<MSGFILTER*>(lParam);
4876 if (WM_KEYDOWN == msgFilter->msg && VK_TAB == msgFilter->wParam)
4877 {
4878 BOOL fShift = 0x8000 & ::GetKeyState(VK_SHIFT);
4879 HWND hwndFocus = ::GetNextDlgTabItem(hWnd, msgFilter->nmhdr.hwndFrom, fShift);
4880 ::SetFocus(hwndFocus);
4881 return 1;
4882 }
4883 break;
4884 }
4885
4886 // Hyperlink clicks from rich-edit control.
4887 case EN_LINK:
4888 return SUCCEEDED(OnRichEditEnLink(lParam, pnmhdr->hwndFrom, hWnd));
4889
4890 // Clicks on a hypertext/syslink control.
4891 case NM_CLICK: __fallthrough;
4892 case NM_RETURN:
4893 if (ControlIsType(pTheme, static_cast<DWORD>(pnmhdr->idFrom), THEME_CONTROL_TYPE_HYPERTEXT))
4894 {
4895 PNMLINK pnmlink = reinterpret_cast<PNMLINK>(lParam);
4896 LITEM litem = pnmlink->item;
4897 ShelExec(litem.szUrl, NULL, L"open", NULL, SW_SHOWDEFAULT, hWnd, NULL);
4898 return 1;
4899 }
4900
4901 return 0;
4902 }
4903 }
4904 break;
4905
4906 case WM_COMMAND:
4907 switch (HIWORD(wParam))
4908 {
4909 case BN_CLICKED:
4910 if (lParam)
4911 {
4912 const THEME_CONTROL* pControl = FindControlFromHWnd(pTheme, (HWND)lParam);
4913 if (pControl && OnButtonClicked(pTheme, hWnd, pControl))
4914 {
4915 return 0;
4916 }
4917 }
4918 break;
4919 }
4920 break;
4921 }
4922 }
4923
4924 return ::DefWindowProcW(hWnd, uMsg, wParam, lParam);
4925}
4926
4927
4928static LRESULT CALLBACK PanelWndProc(
4929 __in HWND hWnd,
4930 __in UINT uMsg,
4931 __in WPARAM wParam,
4932 __in LPARAM lParam
4933 )
4934{
4935 LRESULT lres = 0;
4936 THEME* pTheme = reinterpret_cast<THEME*>(::GetWindowLongPtrW(hWnd, GWLP_USERDATA));
4937
4938 switch (uMsg)
4939 {
4940 case WM_NCCREATE:
4941 {
4942 LPCREATESTRUCTW lpcs = reinterpret_cast<LPCREATESTRUCTW>(lParam);
4943 pTheme = reinterpret_cast<THEME*>(lpcs->lpCreateParams);
4944 ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pTheme));
4945 }
4946 break;
4947
4948 case WM_NCDESTROY:
4949 lres = ::DefWindowProcW(hWnd, uMsg, wParam, lParam);
4950 ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0);
4951 return lres;
4952
4953 case WM_NCHITTEST:
4954 return HTCLIENT;
4955 break;
4956 }
4957
4958 return ControlGroupDefWindowProc(pTheme, hWnd, uMsg, wParam, lParam);
4959}
4960
4961static LRESULT CALLBACK StaticOwnerDrawWndProc(
4962 __in HWND hWnd,
4963 __in UINT uMsg,
4964 __in WPARAM wParam,
4965 __in LPARAM lParam
4966 )
4967{
4968 switch (uMsg)
4969 {
4970 case WM_UPDATEUISTATE:
4971 return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
4972 default:
4973 return (*vpfnStaticOwnerDrawBaseWndProc)(hWnd, uMsg, wParam, lParam);
4974 }
4975}
4976
4977static HRESULT LoadControls(
4978 __in THEME* pTheme,
4979 __in_opt THEME_CONTROL* pParentControl,
4980 __in_ecount_opt(cAssignControlIds) const THEME_ASSIGN_CONTROL_ID* rgAssignControlIds,
4981 __in DWORD cAssignControlIds
4982 )
4983{
4984 HRESULT hr = S_OK;
4985 RECT rcParent = { };
4986 LPWSTR sczText = NULL;
4987 BOOL fStartNewGroup = FALSE;
4988 DWORD cControls = 0;
4989 THEME_CONTROL* rgControls = NULL;
4990 HWND hwndParent = pParentControl ? pParentControl->hWnd : pTheme->hwndParent;
4991 int w = 0;
4992 int h = 0;
4993 int x = 0;
4994 int y = 0;
4995
4996 GetControls(pTheme, pParentControl, cControls, rgControls);
4997 ::GetClientRect(hwndParent, &rcParent);
4998
4999 for (DWORD i = 0; i < cControls; ++i)
5000 {
5001 THEME_CONTROL* pControl = rgControls + i;
5002 THEME_FONT* pControlFont = (pTheme->cFonts > pControl->dwFontId) ? pTheme->rgFonts + pControl->dwFontId : NULL;
5003 THEME_FONT_INSTANCE* pControlFontInstance = NULL;
5004 LPCWSTR wzWindowClass = NULL;
5005 DWORD dwWindowBits = WS_CHILD;
5006 DWORD dwWindowExBits = 0;
5007
5008 if (fStartNewGroup)
5009 {
5010 dwWindowBits |= WS_GROUP;
5011 fStartNewGroup = FALSE;
5012 }
5013
5014 switch (pControl->type)
5015 {
5016 case THEME_CONTROL_TYPE_BILLBOARD:
5017 __fallthrough;
5018 case THEME_CONTROL_TYPE_PANEL:
5019 wzWindowClass = THEME_WC_PANEL;
5020 dwWindowBits |= WS_CHILDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
5021 dwWindowExBits |= WS_EX_TRANSPARENT | WS_EX_CONTROLPARENT;
5022#ifdef DEBUG
5023 StrAllocFormatted(&pControl->sczText, L"Panel '%ls', id: %d", pControl->sczName, pControl->wId);
5024#endif
5025 break;
5026
5027 case THEME_CONTROL_TYPE_CHECKBOX:
5028 dwWindowBits |= BS_AUTOCHECKBOX | BS_MULTILINE; // checkboxes are basically buttons with an extra bit tossed in.
5029 __fallthrough;
5030 case THEME_CONTROL_TYPE_BUTTON:
5031 wzWindowClass = WC_BUTTONW;
5032 if (pControl->hImage || (pTheme->hImage && 0 <= pControl->nSourceX && 0 <= pControl->nSourceY))
5033 {
5034 dwWindowBits |= BS_OWNERDRAW;
5035 pControl->dwInternalStyle |= INTERNAL_CONTROL_STYLE_OWNER_DRAW;
5036 }
5037 break;
5038
5039 case THEME_CONTROL_TYPE_COMBOBOX:
5040 wzWindowClass = WC_COMBOBOXW;
5041 dwWindowBits |= CBS_DROPDOWNLIST | CBS_HASSTRINGS;
5042 break;
5043
5044 case THEME_CONTROL_TYPE_COMMANDLINK:
5045 wzWindowClass = WC_BUTTONW;
5046 dwWindowBits |= BS_COMMANDLINK;
5047 break;
5048
5049 case THEME_CONTROL_TYPE_EDITBOX:
5050 wzWindowClass = WC_EDITW;
5051 dwWindowBits |= ES_LEFT | ES_AUTOHSCROLL;
5052 dwWindowExBits = WS_EX_CLIENTEDGE;
5053 break;
5054
5055 case THEME_CONTROL_TYPE_HYPERLINK: // hyperlinks are basically just owner drawn buttons.
5056 wzWindowClass = THEME_WC_HYPERLINK;
5057 dwWindowBits |= BS_OWNERDRAW | BTNS_NOPREFIX;
5058 break;
5059
5060 case THEME_CONTROL_TYPE_HYPERTEXT:
5061 wzWindowClass = WC_LINK;
5062 dwWindowBits |= LWS_NOPREFIX;
5063 break;
5064
5065 case THEME_CONTROL_TYPE_IMAGE: // images are basically just owner drawn static controls (so we can draw .jpgs and .pngs instead of just bitmaps).
5066 if (pControl->hImage || (pTheme->hImage && 0 <= pControl->nSourceX && 0 <= pControl->nSourceY))
5067 {
5068 wzWindowClass = THEME_WC_STATICOWNERDRAW;
5069 dwWindowBits |= SS_OWNERDRAW;
5070 pControl->dwInternalStyle |= INTERNAL_CONTROL_STYLE_OWNER_DRAW;
5071 }
5072 else
5073 {
5074 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
5075 ThmExitOnRootFailure(hr, "Invalid image or image list coordinates.");
5076 }
5077 break;
5078
5079 case THEME_CONTROL_TYPE_LABEL:
5080 wzWindowClass = WC_STATICW;
5081 break;
5082
5083 case THEME_CONTROL_TYPE_LISTVIEW:
5084 // If thmutil is handling the image list for this listview, tell Windows not to free it when the control is destroyed.
5085 if (pControl->rghImageList[0] || pControl->rghImageList[1] || pControl->rghImageList[2] || pControl->rghImageList[3])
5086 {
5087 pControl->dwStyle |= LVS_SHAREIMAGELISTS;
5088 }
5089 wzWindowClass = WC_LISTVIEWW;
5090 break;
5091
5092 case THEME_CONTROL_TYPE_PROGRESSBAR:
5093 if (pControl->hImage || (pTheme->hImage && 0 <= pControl->nSourceX && 0 <= pControl->nSourceY))
5094 {
5095 wzWindowClass = THEME_WC_STATICOWNERDRAW; // no such thing as an owner drawn progress bar so we'll make our own out of a static control.
5096 dwWindowBits |= SS_OWNERDRAW;
5097 pControl->dwInternalStyle |= INTERNAL_CONTROL_STYLE_OWNER_DRAW;
5098 }
5099 else
5100 {
5101 wzWindowClass = PROGRESS_CLASSW;
5102 }
5103 break;
5104
5105 case THEME_CONTROL_TYPE_RADIOBUTTON:
5106 dwWindowBits |= BS_AUTORADIOBUTTON | BS_MULTILINE;
5107 wzWindowClass = WC_BUTTONW;
5108
5109 if (pControl->fLastRadioButton)
5110 {
5111 fStartNewGroup = TRUE;
5112 }
5113 break;
5114
5115 case THEME_CONTROL_TYPE_RICHEDIT:
5116 if (!vhModuleMsftEdit && !vhModuleRichEd)
5117 {
5118 hr = LoadSystemLibrary(L"Msftedit.dll", &vhModuleMsftEdit);
5119 if (FAILED(hr))
5120 {
5121 hr = LoadSystemLibrary(L"Riched20.dll", &vhModuleRichEd);
5122 ThmExitOnFailure(hr, "Failed to load Rich Edit control library.");
5123 }
5124 }
5125
5126 wzWindowClass = vhModuleMsftEdit ? MSFTEDIT_CLASS : RICHEDIT_CLASSW;
5127 dwWindowBits |= ES_AUTOVSCROLL | ES_MULTILINE | WS_VSCROLL | ES_READONLY;
5128 break;
5129
5130 case THEME_CONTROL_TYPE_STATIC:
5131 wzWindowClass = WC_STATICW;
5132 dwWindowBits |= SS_ETCHEDHORZ;
5133 break;
5134
5135 case THEME_CONTROL_TYPE_TAB:
5136 wzWindowClass = WC_TABCONTROLW;
5137 break;
5138
5139 case THEME_CONTROL_TYPE_TREEVIEW:
5140 wzWindowClass = WC_TREEVIEWW;
5141 break;
5142 }
5143 ThmExitOnNull(wzWindowClass, hr, E_INVALIDDATA, "Failed to configure control %u because of unknown type: %u", i, pControl->type);
5144
5145 // Default control ids to the theme id and its index in the control array, unless there
5146 // is a specific id to assign to a named control.
5147 WORD wControlId = MAKEWORD(i, pTheme->wId);
5148 for (DWORD iAssignControl = 0; pControl->sczName && iAssignControl < cAssignControlIds; ++iAssignControl)
5149 {
5150 if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pControl->sczName, -1, rgAssignControlIds[iAssignControl].wzName, -1))
5151 {
5152 wControlId = rgAssignControlIds[iAssignControl].wId;
5153 break;
5154 }
5155 }
5156
5157 pControl->wId = wControlId;
5158
5159 GetControlDimensions(pControl, &rcParent, &w, &h, &x, &y);
5160
5161 BOOL fVisible = pControl->dwStyle & WS_VISIBLE;
5162 BOOL fDisabled = pControl->dwStyle & WS_DISABLED;
5163
5164 // If the control is supposed to be initially visible and it has a VisibleCondition, check if it's true.
5165 if (fVisible && pControl->sczVisibleCondition && pTheme->pfnEvaluateCondition && !pControl->fDisableVariableFunctionality)
5166 {
5167 hr = pTheme->pfnEvaluateCondition(pControl->sczVisibleCondition, &fVisible, pTheme->pvVariableContext);
5168 ThmExitOnFailure(hr, "Failed to evaluate VisibleCondition: %ls", pControl->sczVisibleCondition);
5169
5170 if (!fVisible)
5171 {
5172 pControl->dwStyle &= ~WS_VISIBLE;
5173 }
5174 }
5175
5176 // Disable controls that aren't visible so their shortcut keys don't trigger.
5177 if (!fVisible)
5178 {
5179 dwWindowBits |= WS_DISABLED;
5180 fDisabled = TRUE;
5181 }
5182
5183 // If the control is supposed to be initially enabled and it has an EnableCondition, check if it's true.
5184 if (!fDisabled && pControl->sczEnableCondition && pTheme->pfnEvaluateCondition && !pControl->fDisableVariableFunctionality)
5185 {
5186 BOOL fEnable = TRUE;
5187
5188 hr = pTheme->pfnEvaluateCondition(pControl->sczEnableCondition, &fEnable, pTheme->pvVariableContext);
5189 ThmExitOnFailure(hr, "Failed to evaluate EnableCondition: %ls", pControl->sczEnableCondition);
5190
5191 fDisabled = !fEnable;
5192 dwWindowBits |= fDisabled ? WS_DISABLED : 0;
5193 }
5194
5195 // Honor the HideWhenDisabled option.
5196 if ((pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_HIDE_WHEN_DISABLED) && fVisible && fDisabled)
5197 {
5198 fVisible = FALSE;
5199 pControl->dwStyle &= ~WS_VISIBLE;
5200 }
5201
5202 pControl->hWnd = ::CreateWindowExW(dwWindowExBits, wzWindowClass, pControl->sczText, pControl->dwStyle | dwWindowBits, x, y, w, h, hwndParent, reinterpret_cast<HMENU>(wControlId), NULL, pTheme);
5203 ThmExitOnNullWithLastError(pControl->hWnd, hr, "Failed to create window.");
5204
5205 if (pControl->sczTooltip)
5206 {
5207 if (!pTheme->hwndTooltip)
5208 {
5209 pTheme->hwndTooltip = ::CreateWindowExW(WS_EX_TOOLWINDOW, TOOLTIPS_CLASSW, NULL, WS_POPUP | TTS_ALWAYSTIP | TTS_BALLOON | TTS_NOPREFIX, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, hwndParent, NULL, NULL, NULL);
5210 }
5211
5212 if (pTheme->hwndTooltip)
5213 {
5214 TOOLINFOW toolinfo = {};
5215 toolinfo.cbSize = sizeof(toolinfo);
5216 toolinfo.hwnd = hwndParent;
5217 toolinfo.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
5218 toolinfo.uId = reinterpret_cast<UINT_PTR>(pControl->hWnd);
5219 toolinfo.lpszText = pControl->sczTooltip;
5220 ::SendMessageW(pTheme->hwndTooltip, TTM_ADDTOOLW, 0, reinterpret_cast<LPARAM>(&toolinfo));
5221 }
5222 }
5223
5224 if (THEME_CONTROL_TYPE_COMMANDLINK == pControl->type)
5225 {
5226 if (pControl->sczNote)
5227 {
5228 ::SendMessageW(pControl->hWnd, BCM_SETNOTE, 0, reinterpret_cast<WPARAM>(pControl->sczNote));
5229 }
5230
5231 if (pControl->hImage)
5232 {
5233 ::SendMessageW(pControl->hWnd, BM_SETIMAGE, IMAGE_BITMAP, reinterpret_cast<LPARAM>(pControl->hImage));
5234 }
5235 else if (pControl->hIcon)
5236 {
5237 ::SendMessageW(pControl->hWnd, BM_SETIMAGE, IMAGE_ICON, reinterpret_cast<LPARAM>(pControl->hIcon));
5238 }
5239 }
5240 else if (THEME_CONTROL_TYPE_EDITBOX == pControl->type)
5241 {
5242 if (pControl->dwInternalStyle & INTERNAL_CONTROL_STYLE_FILESYSTEM_AUTOCOMPLETE)
5243 {
5244 hr = ::SHAutoComplete(pControl->hWnd, SHACF_FILESYS_ONLY);
5245 }
5246 }
5247 else if (THEME_CONTROL_TYPE_LISTVIEW == pControl->type)
5248 {
5249 ::SendMessageW(pControl->hWnd, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, pControl->dwExtendedStyle);
5250
5251 hr = SizeListViewColumns(pControl);
5252 ThmExitOnFailure(hr, "Failed to get size of list view columns.");
5253
5254 for (DWORD j = 0; j < pControl->cColumns; ++j)
5255 {
5256 LVCOLUMNW lvc = { };
5257 lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
5258 lvc.cx = pControl->ptcColumns[j].nWidth;
5259 lvc.iSubItem = j;
5260 lvc.pszText = pControl->ptcColumns[j].pszName;
5261 lvc.fmt = LVCFMT_LEFT;
5262 lvc.cchTextMax = 4;
5263
5264 if (-1 == ::SendMessageW(pControl->hWnd, LVM_INSERTCOLUMNW, (WPARAM) (int) (j), (LPARAM) (const LV_COLUMNW *) (&lvc)))
5265 {
5266 ThmExitWithLastError(hr, "Failed to insert listview column %u into tab control.", j);
5267 }
5268
5269 // Return value tells us the old image list, we don't care.
5270 if (pControl->rghImageList[0])
5271 {
5272 ::SendMessageW(pControl->hWnd, LVM_SETIMAGELIST, static_cast<WPARAM>(LVSIL_NORMAL), reinterpret_cast<LPARAM>(pControl->rghImageList[0]));
5273 }
5274 else if (pControl->rghImageList[1])
5275 {
5276 ::SendMessageW(pControl->hWnd, LVM_SETIMAGELIST, static_cast<WPARAM>(LVSIL_SMALL), reinterpret_cast<LPARAM>(pControl->rghImageList[1]));
5277 }
5278 else if (pControl->rghImageList[2])
5279 {
5280 ::SendMessageW(pControl->hWnd, LVM_SETIMAGELIST, static_cast<WPARAM>(LVSIL_STATE), reinterpret_cast<LPARAM>(pControl->rghImageList[2]));
5281 }
5282 else if (pControl->rghImageList[3])
5283 {
5284 ::SendMessageW(pControl->hWnd, LVM_SETIMAGELIST, static_cast<WPARAM>(LVSIL_GROUPHEADER), reinterpret_cast<LPARAM>(pControl->rghImageList[3]));
5285 }
5286 }
5287 }
5288 else if (THEME_CONTROL_TYPE_RICHEDIT == pControl->type)
5289 {
5290 ::SendMessageW(pControl->hWnd, EM_AUTOURLDETECT, static_cast<WPARAM>(TRUE), 0);
5291 ::SendMessageW(pControl->hWnd, EM_SETEVENTMASK, 0, ENM_KEYEVENTS | ENM_LINK);
5292 }
5293 else if (THEME_CONTROL_TYPE_TAB == pControl->type)
5294 {
5295 ULONG_PTR hbrBackground = 0;
5296 if (THEME_INVALID_ID != pControl->dwFontId)
5297 {
5298 hbrBackground = reinterpret_cast<ULONG_PTR>(pTheme->rgFonts[pControl->dwFontId].hBackground);
5299 }
5300 else
5301 {
5302 hbrBackground = ::GetClassLongPtr(pTheme->hwndParent, GCLP_HBRBACKGROUND);
5303 }
5304 ::SetClassLongPtr(pControl->hWnd, GCLP_HBRBACKGROUND, hbrBackground);
5305
5306 for (DWORD j = 0; j < pControl->cTabs; ++j)
5307 {
5308 TCITEMW tci = { };
5309 tci.mask = TCIF_TEXT | TCIF_IMAGE;
5310 tci.iImage = -1;
5311 tci.pszText = pControl->pttTabs[j].pszName;
5312
5313 if (-1 == ::SendMessageW(pControl->hWnd, TCM_INSERTITEMW, (WPARAM) (int) (j), (LPARAM) (const TC_ITEMW *) (&tci)))
5314 {
5315 ThmExitWithLastError(hr, "Failed to insert tab %u into tab control.", j);
5316 }
5317 }
5318 }
5319
5320 if (pControlFont)
5321 {
5322 hr = EnsureFontInstance(pTheme, pControlFont, &pControlFontInstance);
5323 ThmExitOnFailure(hr, "Failed to get DPI specific font.");
5324
5325 ::SendMessageW(pControl->hWnd, WM_SETFONT, (WPARAM) pControlFontInstance->hFont, FALSE);
5326 }
5327
5328 // Initialize the text on all "application" (non-page) controls, best effort only.
5329 if (pTheme->pfnFormatString && !pControl->wPageId && pControl->sczText && *pControl->sczText)
5330 {
5331 HRESULT hrFormat = pTheme->pfnFormatString(pControl->sczText, &sczText, pTheme->pvVariableContext);
5332 if (SUCCEEDED(hrFormat))
5333 {
5334 ThemeSetTextControl(pTheme, pControl->wId, sczText);
5335 }
5336 }
5337
5338 if (pControl->cControls)
5339 {
5340 hr = LoadControls(pTheme, pControl, rgAssignControlIds, cAssignControlIds);
5341 ThmExitOnFailure(hr, "Failed to load child controls.");
5342 }
5343 }
5344
5345LExit:
5346 ReleaseStr(sczText);
5347
5348 return hr;
5349}
5350
5351static HRESULT LocalizeControls(
5352 __in DWORD cControls,
5353 __in THEME_CONTROL* rgControls,
5354 __in const WIX_LOCALIZATION *pWixLoc
5355 )
5356{
5357 HRESULT hr = S_OK;
5358
5359 for (DWORD i = 0; i < cControls; ++i)
5360 {
5361 THEME_CONTROL* pControl = rgControls + i;
5362 hr = LocalizeControl(pControl, pWixLoc);
5363 ThmExitOnFailure(hr, "Failed to localize control: %ls", pControl->sczName);
5364 }
5365
5366LExit:
5367 return hr;
5368}
5369
5370static HRESULT LocalizeControl(
5371 __in THEME_CONTROL* pControl,
5372 __in const WIX_LOCALIZATION *pWixLoc
5373 )
5374{
5375 HRESULT hr = S_OK;
5376 LOC_CONTROL* pLocControl = NULL;
5377 LPWSTR sczLocStringId = NULL;
5378
5379 if (pControl->sczText && *pControl->sczText)
5380 {
5381 hr = LocLocalizeString(pWixLoc, &pControl->sczText);
5382 ThmExitOnFailure(hr, "Failed to localize control text.");
5383 }
5384 else if (pControl->sczName)
5385 {
5386 LOC_STRING* plocString = NULL;
5387
5388 hr = StrAllocFormatted(&sczLocStringId, L"#(loc.%ls)", pControl->sczName);
5389 ThmExitOnFailure(hr, "Failed to format loc string id: %ls", pControl->sczName);
5390
5391 hr = LocGetString(pWixLoc, sczLocStringId, &plocString);
5392 if (E_NOTFOUND != hr)
5393 {
5394 ThmExitOnFailure(hr, "Failed to get loc string: %ls", pControl->sczName);
5395
5396 hr = StrAllocString(&pControl->sczText, plocString->wzText, 0);
5397 ThmExitOnFailure(hr, "Failed to copy loc string to control: %ls", plocString->wzText);
5398 }
5399 }
5400
5401 if (pControl->sczTooltip && *pControl->sczTooltip)
5402 {
5403 hr = LocLocalizeString(pWixLoc, &pControl->sczTooltip);
5404 ThmExitOnFailure(hr, "Failed to localize control tooltip text.");
5405 }
5406
5407 if (pControl->sczNote && *pControl->sczNote)
5408 {
5409 hr = LocLocalizeString(pWixLoc, &pControl->sczNote);
5410 ThmExitOnFailure(hr, "Failed to localize control note text.");
5411 }
5412
5413 for (DWORD j = 0; j < pControl->cConditionalText; ++j)
5414 {
5415 hr = LocLocalizeString(pWixLoc, &pControl->rgConditionalText[j].sczText);
5416 ThmExitOnFailure(hr, "Failed to localize conditional text.");
5417 }
5418
5419 for (DWORD j = 0; j < pControl->cConditionalNotes; ++j)
5420 {
5421 hr = LocLocalizeString(pWixLoc, &pControl->rgConditionalNotes[j].sczText);
5422 ThmExitOnFailure(hr, "Failed to localize conditional note.");
5423 }
5424
5425 for (DWORD j = 0; j < pControl->cColumns; ++j)
5426 {
5427 hr = LocLocalizeString(pWixLoc, &pControl->ptcColumns[j].pszName);
5428 ThmExitOnFailure(hr, "Failed to localize column text.");
5429 }
5430
5431 for (DWORD j = 0; j < pControl->cTabs; ++j)
5432 {
5433 hr = LocLocalizeString(pWixLoc, &pControl->pttTabs[j].pszName);
5434 ThmExitOnFailure(hr, "Failed to localize tab text.");
5435 }
5436
5437 // Localize control's size, location, and text.
5438 if (pControl->sczName)
5439 {
5440 hr = LocGetControl(pWixLoc, pControl->sczName, &pLocControl);
5441 if (E_NOTFOUND == hr)
5442 {
5443 ExitFunction1(hr = S_OK);
5444 }
5445 ThmExitOnFailure(hr, "Failed to localize control.");
5446
5447 if (LOC_CONTROL_NOT_SET != pLocControl->nX)
5448 {
5449 pControl->nDefaultDpiX = pLocControl->nX;
5450 }
5451
5452 if (LOC_CONTROL_NOT_SET != pLocControl->nY)
5453 {
5454 pControl->nDefaultDpiY = pLocControl->nY;
5455 }
5456
5457 if (LOC_CONTROL_NOT_SET != pLocControl->nWidth)
5458 {
5459 pControl->nDefaultDpiWidth = pLocControl->nWidth;
5460 }
5461
5462 if (LOC_CONTROL_NOT_SET != pLocControl->nHeight)
5463 {
5464 pControl->nDefaultDpiHeight = pLocControl->nHeight;
5465 }
5466
5467 if (pLocControl->wzText && *pLocControl->wzText)
5468 {
5469 hr = StrAllocString(&pControl->sczText, pLocControl->wzText, 0);
5470 ThmExitOnFailure(hr, "Failed to localize control text.");
5471 }
5472 }
5473
5474 hr = LocalizeControls(pControl->cControls, pControl->rgControls, pWixLoc);
5475
5476LExit:
5477 ReleaseStr(sczLocStringId);
5478
5479 return hr;
5480}
5481
5482static HRESULT LoadControlsString(
5483 __in DWORD cControls,
5484 __in THEME_CONTROL* rgControls,
5485 __in HMODULE hResModule
5486 )
5487{
5488 HRESULT hr = S_OK;
5489
5490 for (DWORD i = 0; i < cControls; ++i)
5491 {
5492 THEME_CONTROL* pControl = rgControls + i;
5493 hr = LoadControlString(pControl, hResModule);
5494 ThmExitOnFailure(hr, "Failed to load string for control: %ls", pControl->sczName);
5495 }
5496
5497LExit:
5498 return hr;
5499}
5500
5501static HRESULT LoadControlString(
5502 __in THEME_CONTROL* pControl,
5503 __in HMODULE hResModule
5504 )
5505{
5506 HRESULT hr = S_OK;
5507 if (UINT_MAX != pControl->uStringId)
5508 {
5509 hr = ResReadString(hResModule, pControl->uStringId, &pControl->sczText);
5510 ThmExitOnFailure(hr, "Failed to load control text.");
5511
5512 for (DWORD j = 0; j < pControl->cColumns; ++j)
5513 {
5514 if (UINT_MAX != pControl->ptcColumns[j].uStringId)
5515 {
5516 hr = ResReadString(hResModule, pControl->ptcColumns[j].uStringId, &pControl->ptcColumns[j].pszName);
5517 ThmExitOnFailure(hr, "Failed to load column text.");
5518 }
5519 }
5520
5521 for (DWORD j = 0; j < pControl->cTabs; ++j)
5522 {
5523 if (UINT_MAX != pControl->pttTabs[j].uStringId)
5524 {
5525 hr = ResReadString(hResModule, pControl->pttTabs[j].uStringId, &pControl->pttTabs[j].pszName);
5526 ThmExitOnFailure(hr, "Failed to load tab text.");
5527 }
5528 }
5529 }
5530
5531 hr = LoadControlsString(pControl->cControls, pControl->rgControls, hResModule);
5532
5533LExit:
5534 return hr;
5535}
5536
5537static void ResizeControls(
5538 __in DWORD cControls,
5539 __in THEME_CONTROL* rgControls,
5540 __in const RECT* prcParent
5541 )
5542{
5543 for (DWORD i = 0; i < cControls; ++i)
5544 {
5545 THEME_CONTROL* pControl = rgControls + i;
5546 ResizeControl(pControl, prcParent);
5547 }
5548}
5549
5550static void ResizeControl(
5551 __in THEME_CONTROL* pControl,
5552 __in const RECT* prcParent
5553 )
5554{
5555 int w = 0;
5556 int h = 0;
5557 int x = 0;
5558 int y = 0;
5559 RECT rcControl = { };
5560
5561 GetControlDimensions(pControl, prcParent, &w, &h, &x, &y);
5562 ::SetWindowPos(pControl->hWnd, NULL, x, y, w, h, SWP_NOACTIVATE | SWP_NOZORDER);
5563
5564#ifdef DEBUG
5565 if (THEME_CONTROL_TYPE_BUTTON == pControl->type)
5566 {
5567 Trace(REPORT_STANDARD, "Resizing button (%ls/%ls) to (%d,%d)+(%d,%d) for parent (%d,%d)-(%d,%d)",
5568 pControl->sczName, pControl->sczText, x, y, w, h, prcParent->left, prcParent->top, prcParent->right, prcParent->bottom);
5569 }
5570#endif
5571
5572 if (THEME_CONTROL_TYPE_LISTVIEW == pControl->type)
5573 {
5574 SizeListViewColumns(pControl);
5575
5576 for (DWORD j = 0; j < pControl->cColumns; ++j)
5577 {
5578 if (-1 == ::SendMessageW(pControl->hWnd, LVM_SETCOLUMNWIDTH, (WPARAM) (int) (j), (LPARAM) (pControl->ptcColumns[j].nWidth)))
5579 {
5580 Trace(REPORT_DEBUG, "Failed to resize listview column %u with error %u", j, ::GetLastError());
5581 return;
5582 }
5583 }
5584 }
5585
5586 if (pControl->cControls)
5587 {
5588 ::GetClientRect(pControl->hWnd, &rcControl);
5589 ResizeControls(pControl->cControls, pControl->rgControls, &rcControl);
5590 }
5591}
5592
5593static void ScaleThemeFromWindow(
5594 __in THEME* pTheme,
5595 __in UINT nDpi,
5596 __in int x,
5597 __in int y
5598 )
5599{
5600 DWORD dwStyle = GetWindowStyle(pTheme->hwndParent);
5601 BOOL fMenu = NULL != ::GetMenu(pTheme->hwndParent);
5602 DWORD dwExStyle = GetWindowExStyle(pTheme->hwndParent);
5603
5604 ScaleTheme(pTheme, nDpi, x, y, dwStyle, fMenu, dwExStyle);
5605}
5606
5607static void ScaleTheme(
5608 __in THEME* pTheme,
5609 __in UINT nDpi,
5610 __in int x,
5611 __in int y,
5612 __in DWORD dwStyle,
5613 __in BOOL fMenu,
5614 __in DWORD dwExStyle
5615 )
5616{
5617 RECT rect = { };
5618
5619 pTheme->nDpi = nDpi;
5620
5621 pTheme->nHeight = DpiuScaleValue(pTheme->nDefaultDpiHeight, pTheme->nDpi);
5622 pTheme->nWidth = DpiuScaleValue(pTheme->nDefaultDpiWidth, pTheme->nDpi);
5623 pTheme->nMinimumHeight = DpiuScaleValue(pTheme->nDefaultDpiMinimumHeight, pTheme->nDpi);
5624 pTheme->nMinimumWidth = DpiuScaleValue(pTheme->nDefaultDpiMinimumWidth, pTheme->nDpi);
5625
5626 rect.left = x;
5627 rect.top = y;
5628 rect.right = x + pTheme->nWidth;
5629 rect.bottom = y + pTheme->nHeight;
5630 DpiuAdjustWindowRect(&rect, dwStyle, fMenu, dwExStyle, pTheme->nDpi);
5631 pTheme->nWindowWidth = rect.right - rect.left;
5632 pTheme->nWindowHeight = rect.bottom - rect.top;
5633
5634 ScaleControls(pTheme, pTheme->cControls, pTheme->rgControls, pTheme->nDpi);
5635
5636 if (pTheme->hwndParent)
5637 {
5638 ::SetWindowPos(pTheme->hwndParent, NULL, x, y, pTheme->nWindowWidth, pTheme->nWindowHeight, SWP_NOACTIVATE | SWP_NOZORDER);
5639 }
5640}
5641
5642static void ScaleControls(
5643 __in THEME* pTheme,
5644 __in DWORD cControls,
5645 __in THEME_CONTROL* rgControls,
5646 __in UINT nDpi
5647 )
5648{
5649 for (DWORD i = 0; i < cControls; ++i)
5650 {
5651 THEME_CONTROL* pControl = rgControls + i;
5652 ScaleControl(pTheme, pControl, nDpi);
5653 }
5654}
5655
5656static void ScaleControl(
5657 __in THEME* pTheme,
5658 __in THEME_CONTROL* pControl,
5659 __in UINT nDpi
5660 )
5661{
5662 HRESULT hr = S_OK;
5663 THEME_FONT* pControlFont = (pTheme->cFonts > pControl->dwFontId) ? pTheme->rgFonts + pControl->dwFontId : NULL;
5664 THEME_FONT_INSTANCE* pControlFontInstance = NULL;
5665
5666 if (pControlFont)
5667 {
5668 hr = EnsureFontInstance(pTheme, pControlFont, &pControlFontInstance);
5669 if (SUCCEEDED(hr) && pControl->hWnd)
5670 {
5671 ::SendMessageW(pControl->hWnd, WM_SETFONT, (WPARAM)pControlFontInstance->hFont, FALSE);
5672 }
5673 }
5674
5675 if (THEME_CONTROL_TYPE_LISTVIEW == pControl->type)
5676 {
5677 for (DWORD i = 0; i < pControl->cColumns; ++i)
5678 {
5679 THEME_COLUMN* pColumn = pControl->ptcColumns + i;
5680
5681 pColumn->nBaseWidth = DpiuScaleValue(pColumn->nDefaultDpiBaseWidth, nDpi);
5682 }
5683 }
5684
5685 pControl->nWidth = DpiuScaleValue(pControl->nDefaultDpiWidth, nDpi);
5686 pControl->nHeight = DpiuScaleValue(pControl->nDefaultDpiHeight, nDpi);
5687 pControl->nX = DpiuScaleValue(pControl->nDefaultDpiX, nDpi);
5688 pControl->nY = DpiuScaleValue(pControl->nDefaultDpiY, nDpi);
5689
5690 if (pControl->cControls)
5691 {
5692 ScaleControls(pTheme, pControl->cControls, pControl->rgControls, nDpi);
5693 }
5694}
5695
5696static void UnloadControls(
5697 __in DWORD cControls,
5698 __in THEME_CONTROL* rgControls
5699 )
5700{
5701 for (DWORD i = 0; i < cControls; ++i)
5702 {
5703 THEME_CONTROL* pControl = rgControls + i;
5704 pControl->hWnd = NULL;
5705
5706 UnloadControls(pControl->cControls, pControl->rgControls);
5707 }
5708}
5709
diff --git a/src/libs/dutil/WixToolset.DUtil/timeutil.cpp b/src/libs/dutil/WixToolset.DUtil/timeutil.cpp
new file mode 100644
index 00000000..b7953c94
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/timeutil.cpp
@@ -0,0 +1,385 @@
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// Exit macros
7#define TimeExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_TIMEUTIL, x, s, __VA_ARGS__)
8#define TimeExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_TIMEUTIL, x, s, __VA_ARGS__)
9#define TimeExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_TIMEUTIL, x, s, __VA_ARGS__)
10#define TimeExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_TIMEUTIL, x, s, __VA_ARGS__)
11#define TimeExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_TIMEUTIL, x, s, __VA_ARGS__)
12#define TimeExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_TIMEUTIL, x, s, __VA_ARGS__)
13#define TimeExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_TIMEUTIL, p, x, e, s, __VA_ARGS__)
14#define TimeExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_TIMEUTIL, p, x, s, __VA_ARGS__)
15#define TimeExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_TIMEUTIL, p, x, e, s, __VA_ARGS__)
16#define TimeExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_TIMEUTIL, p, x, s, __VA_ARGS__)
17#define TimeExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_TIMEUTIL, e, x, s, __VA_ARGS__)
18#define TimeExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_TIMEUTIL, g, x, s, __VA_ARGS__)
19
20const LPCWSTR DAY_OF_WEEK[] = { L"Sun", L"Mon", L"Tue", L"Wed", L"Thu", L"Fri", L"Sat" };
21const LPCWSTR MONTH_OF_YEAR[] = { L"None", L"Jan", L"Feb", L"Mar", L"Apr", L"May", L"Jun", L"Jul", L"Aug", L"Sep", L"Oct", L"Nov", L"Dec" };
22enum TIME_PARSER { DayOfWeek, DayOfMonth, MonthOfYear, Year, Hours, Minutes, Seconds, TimeZone };
23enum TIME_PARSERRFC3339 { RFC3339_Year, RFC3339_Month, RFC3339_Day, RFC3339_Hours, RFC3339_Minutes, RFC3339_Seconds, RFC3339_TimeZone };
24
25// prototypes
26static HRESULT DayFromString(
27 __in_z LPCWSTR wzDay,
28 __out WORD* pwDayOfWeek
29 );
30static HRESULT MonthFromString(
31 __in_z LPCWSTR wzMonth,
32 __out WORD* pwMonthOfYear
33 );
34
35
36/********************************************************************
37 TimeFromString - converts string to FILETIME
38
39*******************************************************************/
40extern "C" HRESULT DAPI TimeFromString(
41 __in_z LPCWSTR wzTime,
42 __out FILETIME* pFileTime
43 )
44{
45 Assert(wzTime && pFileTime);
46
47 HRESULT hr = S_OK;
48 LPWSTR pwzTime = NULL;
49
50 SYSTEMTIME sysTime = { };
51 TIME_PARSER timeParser = DayOfWeek;
52
53 LPCWSTR pwzStart = NULL;
54 LPWSTR pwzEnd = NULL;
55
56 hr = StrAllocString(&pwzTime, wzTime, 0);
57 TimeExitOnFailure(hr, "Failed to copy time.");
58
59 pwzStart = pwzEnd = pwzTime;
60 while (pwzEnd && *pwzEnd)
61 {
62 if (L',' == *pwzEnd || L' ' == *pwzEnd || L':' == *pwzEnd)
63 {
64 *pwzEnd = L'\0'; // null terminate
65 ++pwzEnd;
66
67 while (L' ' == *pwzEnd)
68 {
69 ++pwzEnd; // and skip past the blank space
70 }
71
72 switch (timeParser)
73 {
74 case DayOfWeek:
75 hr = DayFromString(pwzStart, &sysTime.wDayOfWeek);
76 TimeExitOnFailure(hr, "Failed to convert string to day: %ls", pwzStart);
77 break;
78
79 case DayOfMonth:
80 sysTime.wDay = (WORD)wcstoul(pwzStart, NULL, 10);
81 break;
82
83 case MonthOfYear:
84 hr = MonthFromString(pwzStart, &sysTime.wMonth);
85 TimeExitOnFailure(hr, "Failed to convert to month: %ls", pwzStart);
86 break;
87
88 case Year:
89 sysTime.wYear = (WORD)wcstoul(pwzStart, NULL, 10);
90 break;
91
92 case Hours:
93 sysTime.wHour = (WORD)wcstoul(pwzStart, NULL, 10);
94 break;
95
96 case Minutes:
97 sysTime.wMinute = (WORD)wcstoul(pwzStart, NULL, 10);
98 break;
99
100 case Seconds:
101 sysTime.wSecond = (WORD)wcstoul(pwzStart, NULL, 10);
102 break;
103
104 case TimeZone:
105 // TODO: do something with this in the future, but this should only hit outside of the while loop.
106 break;
107
108 default:
109 break;
110 }
111
112 pwzStart = pwzEnd;
113 timeParser = (TIME_PARSER)((int)timeParser + 1);
114 }
115
116 ++pwzEnd;
117 }
118
119
120 if (!::SystemTimeToFileTime(&sysTime, pFileTime))
121 {
122 TimeExitWithLastError(hr, "Failed to convert system time to file time.");
123 }
124
125LExit:
126 ReleaseStr(pwzTime);
127
128 return hr;
129}
130
131/********************************************************************
132 TimeFromString3339 - converts string formated in accorance with RFC3339 to FILETIME
133 http://tools.ietf.org/html/rfc3339
134*******************************************************************/
135extern "C" HRESULT DAPI TimeFromString3339(
136 __in_z LPCWSTR wzTime,
137 __out FILETIME* pFileTime
138 )
139{
140 Assert(wzTime && pFileTime);
141
142 HRESULT hr = S_OK;
143 LPWSTR pwzTime = NULL;
144
145 SYSTEMTIME sysTime = { };
146 TIME_PARSERRFC3339 timeParser = RFC3339_Year;
147
148 LPCWSTR pwzStart = NULL;
149 LPWSTR pwzEnd = NULL;
150
151 hr = StrAllocString(&pwzTime, wzTime, 0);
152 TimeExitOnFailure(hr, "Failed to copy time.");
153
154 pwzStart = pwzEnd = pwzTime;
155 while (pwzEnd && *pwzEnd)
156 {
157 if (L'T' == *pwzEnd || L':' == *pwzEnd || L'-' == *pwzEnd)
158 {
159 *pwzEnd = L'\0'; // null terminate
160 ++pwzEnd;
161
162 switch (timeParser)
163 {
164 case RFC3339_Year:
165 sysTime.wYear = (WORD)wcstoul(pwzStart, NULL, 10);
166 break;
167
168 case RFC3339_Month:
169 sysTime.wMonth = (WORD)wcstoul(pwzStart, NULL, 10);
170 break;
171
172 case RFC3339_Day:
173 sysTime.wDay = (WORD)wcstoul(pwzStart, NULL, 10);
174 break;
175
176 case RFC3339_Hours:
177 sysTime.wHour = (WORD)wcstoul(pwzStart, NULL, 10);
178 break;
179
180 case RFC3339_Minutes:
181 sysTime.wMinute = (WORD)wcstoul(pwzStart, NULL, 10);
182 break;
183
184 case RFC3339_Seconds:
185 sysTime.wSecond = (WORD)wcstoul(pwzStart, NULL, 10);
186 break;
187
188 case RFC3339_TimeZone:
189 // TODO: do something with this in the future, but this should only hit outside of the while loop.
190 break;
191
192 default:
193 break;
194 }
195
196 pwzStart = pwzEnd;
197 timeParser = (TIME_PARSERRFC3339)((int)timeParser + 1);
198 }
199
200 ++pwzEnd;
201 }
202
203
204 if (!::SystemTimeToFileTime(&sysTime, pFileTime))
205 {
206 TimeExitWithLastError(hr, "Failed to convert system time to file time.");
207 }
208
209LExit:
210 ReleaseStr(pwzTime);
211
212 return hr;
213}
214/****************************************************************************
215TimeCurrentTime - gets the current time in string format
216
217****************************************************************************/
218extern "C" HRESULT DAPI TimeCurrentTime(
219 __deref_out_z LPWSTR* ppwz,
220 __in BOOL fGMT
221 )
222{
223 SYSTEMTIME st;
224
225 if (fGMT)
226 {
227 ::GetSystemTime(&st);
228 }
229 else
230 {
231 SYSTEMTIME stGMT;
232 TIME_ZONE_INFORMATION tzi;
233
234 ::GetTimeZoneInformation(&tzi);
235 ::GetSystemTime(&stGMT);
236 ::SystemTimeToTzSpecificLocalTime(&tzi, &stGMT, &st);
237 }
238
239 return StrAllocFormatted(ppwz, L"%02d:%02d:%02d", st.wHour, st.wMinute, st.wSecond);
240}
241
242
243/****************************************************************************
244TimeCurrentDateTime - gets the current date and time in string format,
245 per format described in RFC 3339
246****************************************************************************/
247extern "C" HRESULT DAPI TimeCurrentDateTime(
248 __deref_out_z LPWSTR* ppwz,
249 __in BOOL fGMT
250 )
251{
252 SYSTEMTIME st;
253
254 ::GetSystemTime(&st);
255
256 return TimeSystemDateTime(ppwz, &st, fGMT);
257}
258
259
260/****************************************************************************
261TimeSystemDateTime - converts the provided system time struct to string format,
262 per format described in RFC 3339
263****************************************************************************/
264extern "C" HRESULT DAPI TimeSystemDateTime(
265 __deref_out_z LPWSTR* ppwz,
266 __in const SYSTEMTIME *pst,
267 __in BOOL fGMT
268 )
269{
270 DWORD dwAbsBias = 0;
271
272 if (fGMT)
273 {
274 return StrAllocFormatted(ppwz, L"%04hu-%02hu-%02huT%02hu:%02hu:%02huZ", pst->wYear, pst->wMonth, pst->wDay, pst->wHour, pst->wMinute, pst->wSecond);
275 }
276 else
277 {
278 SYSTEMTIME st;
279 TIME_ZONE_INFORMATION tzi;
280
281 ::GetTimeZoneInformation(&tzi);
282 ::SystemTimeToTzSpecificLocalTime(&tzi, pst, &st);
283 dwAbsBias = abs(tzi.Bias);
284
285 return StrAllocFormatted(ppwz, L"%04hu-%02hu-%02huT%02hu:%02hu:%02hu%c%02u:%02u", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, 0 >= tzi.Bias ? L'+' : L'-', dwAbsBias / 60, dwAbsBias % 60);
286 }
287}
288
289
290/****************************************************************************
291TimeSystemToDateTimeString - converts the provided system time struct to
292 string format representing date and time for the specified locale
293****************************************************************************/
294HRESULT DAPI TimeSystemToDateTimeString(
295 __deref_out_z LPWSTR* ppwz,
296 __in const SYSTEMTIME* pst,
297 __in LCID locale
298 )
299{
300 HRESULT hr = S_OK;
301 const WCHAR * DATE_FORMAT = L"MMM dd',' yyyy',' ";
302 const WCHAR * TIME_FORMAT = L"hh':'mm':'ss tt";
303 int iLenDate = 0;
304 int iLenTime = 0;
305
306 iLenDate = ::GetDateFormatW(locale, 0, pst, DATE_FORMAT, NULL, 0);
307 if (0 >= iLenDate)
308 {
309 TimeExitWithLastError(hr, "Failed to get date format with NULL");
310 }
311
312 iLenTime = ::GetTimeFormatW(locale, 0, pst, TIME_FORMAT, NULL, 0);
313 if (0 >= iLenTime)
314 {
315 TimeExitWithLastError(hr, "Failed to get time format with NULL");
316 }
317
318 // Between both lengths we account for 2 null terminators, and only need one, so we subtract one
319 hr = StrAlloc(ppwz, iLenDate + iLenTime - 1);
320 TimeExitOnFailure(hr, "Failed to allocate string");
321
322 if (!::GetDateFormatW(locale, 0, pst, DATE_FORMAT, *ppwz, iLenDate))
323 {
324 TimeExitWithLastError(hr, "Failed to get date format with buffer");
325 }
326 // Space to separate them
327 (*ppwz)[iLenDate - 1] = ' ';
328
329 if (!::GetTimeFormatW(locale, 0, pst, TIME_FORMAT, (*ppwz) + iLenDate - 1, iLenTime))
330 {
331 TimeExitWithLastError(hr, "Failed to get time format with buffer");
332 }
333
334LExit:
335 return hr;
336}
337
338/********************************************************************
339 DayFromString - converts string to day
340
341*******************************************************************/
342static HRESULT DayFromString(
343 __in_z LPCWSTR wzDay,
344 __out WORD* pwDayOfWeek
345 )
346{
347 HRESULT hr = E_INVALIDARG; // assume we won't find a matching name
348
349 for (WORD i = 0; i < countof(DAY_OF_WEEK); ++i)
350 {
351 if (0 == lstrcmpW(wzDay, DAY_OF_WEEK[i]))
352 {
353 *pwDayOfWeek = i;
354 hr = S_OK;
355 break;
356 }
357 }
358
359 return hr;
360}
361
362
363/********************************************************************
364 MonthFromString - converts string to month
365
366*******************************************************************/
367static HRESULT MonthFromString(
368 __in_z LPCWSTR wzMonth,
369 __out WORD* pwMonthOfYear
370 )
371{
372 HRESULT hr = E_INVALIDARG; // assume we won't find a matching name
373
374 for (WORD i = 0; i < countof(MONTH_OF_YEAR); ++i)
375 {
376 if (0 == lstrcmpW(wzMonth, MONTH_OF_YEAR[i]))
377 {
378 *pwMonthOfYear = i;
379 hr = S_OK;
380 break;
381 }
382 }
383
384 return hr;
385}
diff --git a/src/libs/dutil/WixToolset.DUtil/uncutil.cpp b/src/libs/dutil/WixToolset.DUtil/uncutil.cpp
new file mode 100644
index 00000000..415ea198
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/uncutil.cpp
@@ -0,0 +1,69 @@
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// Exit macros
7#define UncExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_UNCUTIL, x, s, __VA_ARGS__)
8#define UncExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_UNCUTIL, x, s, __VA_ARGS__)
9#define UncExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_UNCUTIL, x, s, __VA_ARGS__)
10#define UncExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_UNCUTIL, x, s, __VA_ARGS__)
11#define UncExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_UNCUTIL, x, s, __VA_ARGS__)
12#define UncExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_UNCUTIL, x, s, __VA_ARGS__)
13#define UncExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_UNCUTIL, p, x, e, s, __VA_ARGS__)
14#define UncExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_UNCUTIL, p, x, s, __VA_ARGS__)
15#define UncExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_UNCUTIL, p, x, e, s, __VA_ARGS__)
16#define UncExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_UNCUTIL, p, x, s, __VA_ARGS__)
17#define UncExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_UNCUTIL, e, x, s, __VA_ARGS__)
18#define UncExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_UNCUTIL, g, x, s, __VA_ARGS__)
19
20DAPI_(HRESULT) UncConvertFromMountedDrive(
21 __inout LPWSTR *psczUNCPath,
22 __in LPCWSTR sczMountedDrivePath
23 )
24{
25 HRESULT hr = S_OK;
26 DWORD dwLength = 0;
27 DWORD er = ERROR_SUCCESS;
28 LPWSTR sczDrive = NULL;
29
30 // Only copy drive letter and colon
31 hr = StrAllocString(&sczDrive, sczMountedDrivePath, 2);
32 UncExitOnFailure(hr, "Failed to copy drive");
33
34 // ERROR_NOT_CONNECTED means it's not a mapped drive
35 er = ::WNetGetConnectionW(sczDrive, NULL, &dwLength);
36 if (ERROR_MORE_DATA == er)
37 {
38 er = ERROR_SUCCESS;
39
40 hr = StrAlloc(psczUNCPath, dwLength);
41 UncExitOnFailure(hr, "Failed to allocate string to get raw UNC path of length %u", dwLength);
42
43 er = ::WNetGetConnectionW(sczDrive, *psczUNCPath, &dwLength);
44 if (ERROR_CONNECTION_UNAVAIL == er)
45 {
46 // This means the drive is remembered but not currently connected, this can mean the location is accessible via UNC path but not via mounted drive path
47 er = ERROR_SUCCESS;
48 }
49 UncExitOnWin32Error(er, hr, "::WNetGetConnectionW() failed with buffer provided on drive %ls", sczDrive);
50
51 // Skip drive letter and colon
52 hr = StrAllocConcat(psczUNCPath, sczMountedDrivePath + 2, 0);
53 UncExitOnFailure(hr, "Failed to copy rest of database path");
54 }
55 else
56 {
57 if (ERROR_SUCCESS == er)
58 {
59 er = ERROR_NO_DATA;
60 }
61
62 UncExitOnWin32Error(er, hr, "::WNetGetConnectionW() failed on drive %ls", sczDrive);
63 }
64
65LExit:
66 ReleaseStr(sczDrive);
67
68 return hr;
69}
diff --git a/src/libs/dutil/WixToolset.DUtil/uriutil.cpp b/src/libs/dutil/WixToolset.DUtil/uriutil.cpp
new file mode 100644
index 00000000..7913c22e
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/uriutil.cpp
@@ -0,0 +1,553 @@
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// Exit macros
7#define UriExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_URIUTIL, x, s, __VA_ARGS__)
8#define UriExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_URIUTIL, x, s, __VA_ARGS__)
9#define UriExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_URIUTIL, x, s, __VA_ARGS__)
10#define UriExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_URIUTIL, x, s, __VA_ARGS__)
11#define UriExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_URIUTIL, x, s, __VA_ARGS__)
12#define UriExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_URIUTIL, x, s, __VA_ARGS__)
13#define UriExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_URIUTIL, p, x, e, s, __VA_ARGS__)
14#define UriExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_URIUTIL, p, x, s, __VA_ARGS__)
15#define UriExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_URIUTIL, p, x, e, s, __VA_ARGS__)
16#define UriExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_URIUTIL, p, x, s, __VA_ARGS__)
17#define UriExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_URIUTIL, e, x, s, __VA_ARGS__)
18#define UriExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_URIUTIL, g, x, s, __VA_ARGS__)
19
20
21//
22// UriCanonicalize - canonicalizes a URI.
23//
24extern "C" HRESULT DAPI UriCanonicalize(
25 __inout_z LPWSTR* psczUri
26 )
27{
28 HRESULT hr = S_OK;
29 WCHAR wz[INTERNET_MAX_URL_LENGTH] = { };
30 DWORD cch = countof(wz);
31
32 if (!::InternetCanonicalizeUrlW(*psczUri, wz, &cch, ICU_DECODE))
33 {
34 UriExitWithLastError(hr, "Failed to canonicalize URI.");
35 }
36
37 hr = StrAllocString(psczUri, wz, cch);
38 UriExitOnFailure(hr, "Failed copy canonicalized URI.");
39
40LExit:
41 return hr;
42}
43
44
45//
46// UriCrack - cracks a URI into constituent parts.
47//
48extern "C" HRESULT DAPI UriCrack(
49 __in_z LPCWSTR wzUri,
50 __out_opt INTERNET_SCHEME* pScheme,
51 __deref_opt_out_z LPWSTR* psczHostName,
52 __out_opt INTERNET_PORT* pPort,
53 __deref_opt_out_z LPWSTR* psczUser,
54 __deref_opt_out_z LPWSTR* psczPassword,
55 __deref_opt_out_z LPWSTR* psczPath,
56 __deref_opt_out_z LPWSTR* psczQueryString
57 )
58{
59 HRESULT hr = S_OK;
60 URL_COMPONENTSW components = { };
61 WCHAR wzHostName[INTERNET_MAX_HOST_NAME_LENGTH + 1];
62 WCHAR wzUserName[INTERNET_MAX_USER_NAME_LENGTH + 1];
63 WCHAR wzPassword[INTERNET_MAX_PASSWORD_LENGTH + 1];
64 WCHAR wzPath[INTERNET_MAX_PATH_LENGTH + 1];
65 WCHAR wzQueryString[INTERNET_MAX_PATH_LENGTH + 1];
66
67 components.dwStructSize = sizeof(URL_COMPONENTSW);
68
69 if (psczHostName)
70 {
71 components.lpszHostName = wzHostName;
72 components.dwHostNameLength = countof(wzHostName);
73 }
74
75 if (psczUser)
76 {
77 components.lpszUserName = wzUserName;
78 components.dwUserNameLength = countof(wzUserName);
79 }
80
81 if (psczPassword)
82 {
83 components.lpszPassword = wzPassword;
84 components.dwPasswordLength = countof(wzPassword);
85 }
86
87 if (psczPath)
88 {
89 components.lpszUrlPath = wzPath;
90 components.dwUrlPathLength = countof(wzPath);
91 }
92
93 if (psczQueryString)
94 {
95 components.lpszExtraInfo = wzQueryString;
96 components.dwExtraInfoLength = countof(wzQueryString);
97 }
98
99 if (!::InternetCrackUrlW(wzUri, 0, ICU_DECODE | ICU_ESCAPE, &components))
100 {
101 UriExitWithLastError(hr, "Failed to crack URI.");
102 }
103
104 if (pScheme)
105 {
106 *pScheme = components.nScheme;
107 }
108
109 if (psczHostName)
110 {
111 hr = StrAllocString(psczHostName, components.lpszHostName, components.dwHostNameLength);
112 UriExitOnFailure(hr, "Failed to copy host name.");
113 }
114
115 if (pPort)
116 {
117 *pPort = components.nPort;
118 }
119
120 if (psczUser)
121 {
122 hr = StrAllocString(psczUser, components.lpszUserName, components.dwUserNameLength);
123 UriExitOnFailure(hr, "Failed to copy user name.");
124 }
125
126 if (psczPassword)
127 {
128 hr = StrAllocString(psczPassword, components.lpszPassword, components.dwPasswordLength);
129 UriExitOnFailure(hr, "Failed to copy password.");
130 }
131
132 if (psczPath)
133 {
134 hr = StrAllocString(psczPath, components.lpszUrlPath, components.dwUrlPathLength);
135 UriExitOnFailure(hr, "Failed to copy path.");
136 }
137
138 if (psczQueryString)
139 {
140 hr = StrAllocString(psczQueryString, components.lpszExtraInfo, components.dwExtraInfoLength);
141 UriExitOnFailure(hr, "Failed to copy query string.");
142 }
143
144LExit:
145 return hr;
146}
147
148
149//
150// UriCrackEx - cracks a URI into URI_INFO.
151//
152extern "C" HRESULT DAPI UriCrackEx(
153 __in_z LPCWSTR wzUri,
154 __in URI_INFO* pUriInfo
155 )
156{
157 HRESULT hr = S_OK;
158
159 hr = UriCrack(wzUri, &pUriInfo->scheme, &pUriInfo->sczHostName, &pUriInfo->port, &pUriInfo->sczUser, &pUriInfo->sczPassword, &pUriInfo->sczPath, &pUriInfo->sczQueryString);
160 UriExitOnFailure(hr, "Failed to crack URI.");
161
162LExit:
163 return hr;
164}
165
166
167//
168// UriInfoUninitialize - frees the memory in a URI_INFO struct.
169//
170extern "C" void DAPI UriInfoUninitialize(
171 __in URI_INFO* pUriInfo
172 )
173{
174 ReleaseStr(pUriInfo->sczHostName);
175 ReleaseStr(pUriInfo->sczUser);
176 ReleaseStr(pUriInfo->sczPassword);
177 ReleaseStr(pUriInfo->sczPath);
178 ReleaseStr(pUriInfo->sczQueryString);
179 memset(pUriInfo, 0, sizeof(URI_INFO));
180}
181
182
183//
184// UriCreate - creates a URI from constituent parts.
185//
186extern "C" HRESULT DAPI UriCreate(
187 __inout_z LPWSTR* psczUri,
188 __in INTERNET_SCHEME scheme,
189 __in_z_opt LPWSTR wzHostName,
190 __in INTERNET_PORT port,
191 __in_z_opt LPWSTR wzUser,
192 __in_z_opt LPWSTR wzPassword,
193 __in_z_opt LPWSTR wzPath,
194 __in_z_opt LPWSTR wzQueryString
195 )
196{
197 HRESULT hr = S_OK;
198 WCHAR wz[INTERNET_MAX_URL_LENGTH] = { };
199 DWORD cch = countof(wz);
200 URL_COMPONENTSW components = { };
201
202 components.dwStructSize = sizeof(URL_COMPONENTSW);
203 components.nScheme = scheme;
204 components.lpszHostName = wzHostName;
205 components.nPort = port;
206 components.lpszUserName = wzUser;
207 components.lpszPassword = wzPassword;
208 components.lpszUrlPath = wzPath;
209 components.lpszExtraInfo = wzQueryString;
210
211 if (!::InternetCreateUrlW(&components, ICU_ESCAPE, wz, &cch))
212 {
213 UriExitWithLastError(hr, "Failed to create URI.");
214 }
215
216 hr = StrAllocString(psczUri, wz, cch);
217 UriExitOnFailure(hr, "Failed copy created URI.");
218
219LExit:
220 return hr;
221}
222
223
224//
225// UriGetServerAndResource - gets the server and resource as independent strings from a URI.
226//
227// NOTE: This function is useful for the InternetConnect/HttpRequest APIs.
228//
229extern "C" HRESULT DAPI UriGetServerAndResource(
230 __in_z LPCWSTR wzUri,
231 __out_z LPWSTR* psczServer,
232 __out_z LPWSTR* psczResource
233 )
234{
235 HRESULT hr = S_OK;
236 INTERNET_SCHEME scheme = INTERNET_SCHEME_UNKNOWN;
237 LPWSTR sczHostName = NULL;
238 INTERNET_PORT port = INTERNET_INVALID_PORT_NUMBER;
239 LPWSTR sczUser = NULL;
240 LPWSTR sczPassword = NULL;
241 LPWSTR sczPath = NULL;
242 LPWSTR sczQueryString = NULL;
243
244 hr = UriCrack(wzUri, &scheme, &sczHostName, &port, &sczUser, &sczPassword, &sczPath, &sczQueryString);
245 UriExitOnFailure(hr, "Failed to crack URI.");
246
247 hr = UriCreate(psczServer, scheme, sczHostName, port, sczUser, sczPassword, NULL, NULL);
248 UriExitOnFailure(hr, "Failed to allocate server URI.");
249
250 hr = UriCreate(psczResource, INTERNET_SCHEME_UNKNOWN, NULL, INTERNET_INVALID_PORT_NUMBER, NULL, NULL, sczPath, sczQueryString);
251 UriExitOnFailure(hr, "Failed to allocate resource URI.");
252
253LExit:
254 ReleaseStr(sczQueryString);
255 ReleaseStr(sczPath);
256 ReleaseStr(sczPassword);
257 ReleaseStr(sczUser);
258 ReleaseStr(sczHostName);
259
260 return hr;
261}
262
263
264//
265// UriFile - returns the file part of the URI.
266//
267extern "C" HRESULT DAPI UriFile(
268 __deref_out_z LPWSTR* psczFile,
269 __in_z LPCWSTR wzUri
270 )
271{
272 HRESULT hr = S_OK;
273 WCHAR wz[MAX_PATH + 1];
274 DWORD cch = countof(wz);
275 URL_COMPONENTSW uc = { };
276
277 uc.dwStructSize = sizeof(uc);
278 uc.lpszUrlPath = wz;
279 uc.dwUrlPathLength = cch;
280
281 if (!::InternetCrackUrlW(wzUri, 0, ICU_DECODE | ICU_ESCAPE, &uc))
282 {
283 UriExitWithLastError(hr, "Failed to crack URI.");
284 }
285
286 // Copy only the file name. Fortunately, PathFile() understands that
287 // forward slashes can be directory separators like backslashes.
288 hr = StrAllocString(psczFile, PathFile(wz), 0);
289 UriExitOnFailure(hr, "Failed to copy file name");
290
291LExit:
292 return hr;
293}
294
295
296/*******************************************************************
297 UriProtocol - determines the protocol of a URI.
298
299********************************************************************/
300extern "C" HRESULT DAPI UriProtocol(
301 __in_z LPCWSTR wzUri,
302 __out URI_PROTOCOL* pProtocol
303 )
304{
305 Assert(wzUri && *wzUri);
306 Assert(pProtocol);
307
308 HRESULT hr = S_OK;
309
310 if ((L'h' == wzUri[0] || L'H' == wzUri[0]) &&
311 (L't' == wzUri[1] || L'T' == wzUri[1]) &&
312 (L't' == wzUri[2] || L'T' == wzUri[2]) &&
313 (L'p' == wzUri[3] || L'P' == wzUri[3]) &&
314 (L's' == wzUri[4] || L'S' == wzUri[4]) &&
315 L':' == wzUri[5] &&
316 L'/' == wzUri[6] &&
317 L'/' == wzUri[7])
318 {
319 *pProtocol = URI_PROTOCOL_HTTPS;
320 }
321 else if ((L'h' == wzUri[0] || L'H' == wzUri[0]) &&
322 (L't' == wzUri[1] || L'T' == wzUri[1]) &&
323 (L't' == wzUri[2] || L'T' == wzUri[2]) &&
324 (L'p' == wzUri[3] || L'P' == wzUri[3]) &&
325 L':' == wzUri[4] &&
326 L'/' == wzUri[5] &&
327 L'/' == wzUri[6])
328 {
329 *pProtocol = URI_PROTOCOL_HTTP;
330 }
331 else if ((L'f' == wzUri[0] || L'F' == wzUri[0]) &&
332 (L't' == wzUri[1] || L'T' == wzUri[1]) &&
333 (L'p' == wzUri[2] || L'P' == wzUri[2]) &&
334 L':' == wzUri[3] &&
335 L'/' == wzUri[4] &&
336 L'/' == wzUri[5])
337 {
338 *pProtocol = URI_PROTOCOL_FTP;
339 }
340 else if ((L'f' == wzUri[0] || L'F' == wzUri[0]) &&
341 (L'i' == wzUri[1] || L'I' == wzUri[1]) &&
342 (L'l' == wzUri[2] || L'L' == wzUri[2]) &&
343 (L'e' == wzUri[3] || L'E' == wzUri[3]) &&
344 L':' == wzUri[4] &&
345 L'/' == wzUri[5] &&
346 L'/' == wzUri[6])
347 {
348 *pProtocol = URI_PROTOCOL_FILE;
349 }
350 else
351 {
352 *pProtocol = URI_PROTOCOL_UNKNOWN;
353 }
354
355 return hr;
356}
357
358
359/*******************************************************************
360 UriRoot - returns the root of the path specified in the URI.
361
362 examples:
363 file:///C:\path\path -> C:\
364 file://server/share/path/path -> \\server\share
365 http://www.example.com/path/path -> http://www.example.com/
366 ftp://ftp.example.com/path/path -> ftp://www.example.com/
367
368 NOTE: This function should only be used on cannonicalized URIs.
369 It does not cannonicalize itself.
370********************************************************************/
371extern "C" HRESULT DAPI UriRoot(
372 __in_z LPCWSTR wzUri,
373 __out LPWSTR* ppwzRoot,
374 __out_opt URI_PROTOCOL* pProtocol
375 )
376{
377 Assert(wzUri && *wzUri);
378 Assert(ppwzRoot);
379
380 HRESULT hr = S_OK;
381 URI_PROTOCOL protocol = URI_PROTOCOL_UNKNOWN;
382 LPCWSTR pwcSlash = NULL;
383
384 hr = UriProtocol(wzUri, &protocol);
385 UriExitOnFailure(hr, "Invalid URI.");
386
387 switch (protocol)
388 {
389 case URI_PROTOCOL_FILE:
390 if (L'/' == wzUri[7]) // file path
391 {
392 if (((L'a' <= wzUri[8] && L'z' >= wzUri[8]) || (L'A' <= wzUri[8] && L'Z' >= wzUri[8])) && L':' == wzUri[9])
393 {
394 hr = StrAlloc(ppwzRoot, 4);
395 UriExitOnFailure(hr, "Failed to allocate string for root of URI.");
396 *ppwzRoot[0] = wzUri[8];
397 *ppwzRoot[1] = L':';
398 *ppwzRoot[2] = L'\\';
399 *ppwzRoot[3] = L'\0';
400 }
401 else
402 {
403 hr = E_INVALIDARG;
404 UriExitOnFailure(hr, "Invalid file path in URI.");
405 }
406 }
407 else // UNC share
408 {
409 pwcSlash = wcschr(wzUri + 8, L'/');
410 if (!pwcSlash)
411 {
412 hr = E_INVALIDARG;
413 UriExitOnFailure(hr, "Invalid server name in URI.");
414 }
415 else
416 {
417 hr = StrAllocString(ppwzRoot, L"\\\\", 64);
418 UriExitOnFailure(hr, "Failed to allocate string for root of URI.");
419
420 pwcSlash = wcschr(pwcSlash + 1, L'/');
421 if (pwcSlash)
422 {
423 hr = StrAllocConcat(ppwzRoot, wzUri + 8, pwcSlash - wzUri - 8);
424 UriExitOnFailure(hr, "Failed to add server/share to root of URI.");
425 }
426 else
427 {
428 hr = StrAllocConcat(ppwzRoot, wzUri + 8, 0);
429 UriExitOnFailure(hr, "Failed to add server/share to root of URI.");
430 }
431
432 // replace all slashes with backslashes to be truly UNC.
433 for (LPWSTR pwc = *ppwzRoot; pwc && *pwc; ++pwc)
434 {
435 if (L'/' == *pwc)
436 {
437 *pwc = L'\\';
438 }
439 }
440 }
441 }
442 break;
443
444 case URI_PROTOCOL_FTP:
445 pwcSlash = wcschr(wzUri + 6, L'/');
446 if (pwcSlash)
447 {
448 hr = StrAllocString(ppwzRoot, wzUri, pwcSlash - wzUri);
449 UriExitOnFailure(hr, "Failed allocate root from URI.");
450 }
451 else
452 {
453 hr = StrAllocString(ppwzRoot, wzUri, 0);
454 UriExitOnFailure(hr, "Failed allocate root from URI.");
455 }
456 break;
457
458 case URI_PROTOCOL_HTTP:
459 pwcSlash = wcschr(wzUri + 7, L'/');
460 if (pwcSlash)
461 {
462 hr = StrAllocString(ppwzRoot, wzUri, pwcSlash - wzUri);
463 UriExitOnFailure(hr, "Failed allocate root from URI.");
464 }
465 else
466 {
467 hr = StrAllocString(ppwzRoot, wzUri, 0);
468 UriExitOnFailure(hr, "Failed allocate root from URI.");
469 }
470 break;
471
472 default:
473 hr = E_INVALIDARG;
474 UriExitOnFailure(hr, "Unknown URI protocol.");
475 }
476
477 if (pProtocol)
478 {
479 *pProtocol = protocol;
480 }
481
482LExit:
483 return hr;
484}
485
486
487extern "C" HRESULT DAPI UriResolve(
488 __in_z LPCWSTR wzUri,
489 __in_opt LPCWSTR wzBaseUri,
490 __out LPWSTR* ppwzResolvedUri,
491 __out_opt URI_PROTOCOL* pResolvedProtocol
492 )
493{
494 UNREFERENCED_PARAMETER(wzUri);
495 UNREFERENCED_PARAMETER(wzBaseUri);
496 UNREFERENCED_PARAMETER(ppwzResolvedUri);
497 UNREFERENCED_PARAMETER(pResolvedProtocol);
498
499 HRESULT hr = E_NOTIMPL;
500#if 0
501 URI_PROTOCOL protocol = URI_PROTOCOL_UNKNOWN;
502
503 hr = UriProtocol(wzUri, &protocol);
504 UriExitOnFailure(hr, "Failed to determine protocol for URL: %ls", wzUri);
505
506 UriExitOnNull(ppwzResolvedUri, hr, E_INVALIDARG, "Failed to resolve URI, because no method of output was provided");
507
508 if (URI_PROTOCOL_UNKNOWN == protocol)
509 {
510 UriExitOnNull(wzBaseUri, hr, E_INVALIDARG, "Failed to resolve URI - base URI provided was NULL");
511
512 if (L'/' == *wzUri || L'\\' == *wzUri)
513 {
514 hr = UriRoot(wzBaseUri, ppwzResolvedUri, &protocol);
515 UriExitOnFailure(hr, "Failed to get root from URI: %ls", wzBaseUri);
516
517 hr = StrAllocConcat(ppwzResolvedUri, wzUri, 0);
518 UriExitOnFailure(hr, "Failed to concat file to base URI.");
519 }
520 else
521 {
522 hr = UriProtocol(wzBaseUri, &protocol);
523 UriExitOnFailure(hr, "Failed to get protocol of base URI: %ls", wzBaseUri);
524
525 LPCWSTR pwcFile = const_cast<LPCWSTR> (UriFile(wzBaseUri));
526 if (!pwcFile)
527 {
528 hr = E_INVALIDARG;
529 UriExitOnFailure(hr, "Failed to get file from base URI: %ls", wzBaseUri);
530 }
531
532 hr = StrAllocString(ppwzResolvedUri, wzBaseUri, pwcFile - wzBaseUri);
533 UriExitOnFailure(hr, "Failed to allocate string for resolved URI.");
534
535 hr = StrAllocConcat(ppwzResolvedUri, wzUri, 0);
536 UriExitOnFailure(hr, "Failed to concat file to resolved URI.");
537 }
538 }
539 else
540 {
541 hr = StrAllocString(ppwzResolvedUri, wzUri, 0);
542 UriExitOnFailure(hr, "Failed to copy resolved URI.");
543 }
544
545 if (pResolvedProtocol)
546 {
547 *pResolvedProtocol = protocol;
548 }
549
550LExit:
551#endif
552 return hr;
553}
diff --git a/src/libs/dutil/WixToolset.DUtil/userutil.cpp b/src/libs/dutil/WixToolset.DUtil/userutil.cpp
new file mode 100644
index 00000000..ca6d5480
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/userutil.cpp
@@ -0,0 +1,300 @@
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// UserExit macros
7#define UserExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_USERUTIL, x, s, __VA_ARGS__)
8#define UserExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_USERUTIL, x, s, __VA_ARGS__)
9#define UserExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_USERUTIL, x, s, __VA_ARGS__)
10#define UserExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_USERUTIL, x, s, __VA_ARGS__)
11#define UserExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_USERUTIL, x, s, __VA_ARGS__)
12#define UserExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_USERUTIL, x, s, __VA_ARGS__)
13#define UserExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_USERUTIL, p, x, e, s, __VA_ARGS__)
14#define UserExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_USERUTIL, p, x, s, __VA_ARGS__)
15#define UserExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_USERUTIL, p, x, e, s, __VA_ARGS__)
16#define UserExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_USERUTIL, p, x, s, __VA_ARGS__)
17#define UserExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_USERUTIL, e, x, s, __VA_ARGS__)
18#define UserExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_USERUTIL, g, x, s, __VA_ARGS__)
19
20static BOOL CheckIsMemberHelper(
21 __in_z LPCWSTR pwzGroupUserDomain,
22 __in_ecount(cguiGroupData) const GROUP_USERS_INFO_0 *pguiGroupData,
23 __in DWORD cguiGroupData
24 );
25
26/*******************************************************************
27 UserBuildDomainUserName - builds a DOMAIN\USERNAME string
28
29********************************************************************/
30extern "C" HRESULT DAPI UserBuildDomainUserName(
31 __out_ecount_z(cchDest) LPWSTR wzDest,
32 __in int cchDest,
33 __in_z LPCWSTR pwzName,
34 __in_z LPCWSTR pwzDomain
35 )
36{
37 HRESULT hr = S_OK;
38 DWORD cchLeft = cchDest;
39 WCHAR* pwz = wzDest;
40 DWORD cchWz = cchDest;
41 DWORD cch;
42
43 cch = lstrlenW(pwzDomain);
44 if (cch >= cchLeft)
45 {
46 hr = ERROR_MORE_DATA;
47 UserExitOnFailure(hr, "Buffer size is not big enough to hold domain name: %ls", pwzDomain);
48 }
49 else if (cch > 0)
50 {
51 // handle the domain case
52
53 hr = ::StringCchCopyNW(pwz, cchWz, pwzDomain, cchLeft - 1); // last parameter does not include '\0'
54 UserExitOnFailure(hr, "Failed to copy Domain onto string.");
55
56 cchLeft -= cch;
57 pwz += cch;
58 cchWz -= cch;
59
60 if (1 >= cchLeft)
61 {
62 hr = ERROR_MORE_DATA;
63 UserExitOnFailure(hr, "Insufficient buffer size while building domain user name");
64 }
65
66 hr = ::StringCchCopyNW(pwz, cchWz, L"\\", cchLeft - 1); // last parameter does not include '\0'
67 UserExitOnFailure(hr, "Failed to copy backslash onto string.");
68
69 --cchLeft;
70 ++pwz;
71 --cchWz;
72 }
73
74 cch = lstrlenW(pwzName);
75 if (cch >= cchLeft)
76 {
77 hr = ERROR_MORE_DATA;
78 UserExitOnFailure(hr, "Buffer size is not big enough to hold user name: %ls", pwzName);
79 }
80
81 hr = ::StringCchCopyNW(pwz, cchWz, pwzName, cchLeft - 1); // last parameter does not include '\0'
82 UserExitOnFailure(hr, "Failed to copy User name onto string.");
83
84LExit:
85 return hr;
86}
87
88
89/*******************************************************************
90 Checks whether a user is a member of a group - outputs the result via lpfMember
91********************************************************************/
92extern "C" HRESULT DAPI UserCheckIsMember(
93 __in_z LPCWSTR pwzName,
94 __in_z LPCWSTR pwzDomain,
95 __in_z LPCWSTR pwzGroupName,
96 __in_z LPCWSTR pwzGroupDomain,
97 __out LPBOOL lpfMember
98 )
99{
100 HRESULT hr = S_OK;
101 UINT er = ERROR_SUCCESS;
102
103 DWORD dwRead = 0;
104 DWORD dwTotal = 0;
105 LPCWSTR wz = NULL;
106 GROUP_USERS_INFO_0 *pguiGroupData = NULL;
107 WCHAR wzGroupUserDomain[MAX_DARWIN_COLUMN + 1]; // GROUPDOMAIN\GROUPNAME
108 WCHAR wzUserDomain[MAX_DARWIN_COLUMN + 1]; // USERDOMAIN\USERNAME
109 BSTR bstrUser = NULL;
110 BSTR bstrGroup = NULL;
111
112 IADsGroup *pGroup = NULL;
113 VARIANT_BOOL vtBoolResult = VARIANT_FALSE;
114
115 hr = UserBuildDomainUserName(wzGroupUserDomain, countof(wzGroupUserDomain), pwzGroupName, pwzGroupDomain);
116 UserExitOnFailure(hr, "Failed to build group name from group domain %ls, group name %ls", pwzGroupDomain, pwzGroupName);
117
118 hr = UserBuildDomainUserName(wzUserDomain, countof(wzUserDomain), pwzName, pwzDomain);
119 UserExitOnFailure(hr, "Failed to build group name from group domain %ls, group name %ls", pwzGroupDomain, pwzGroupName);
120
121 if (pwzDomain && *pwzDomain)
122 {
123 wz = pwzDomain;
124 }
125
126 er = ::NetUserGetGroups(wz, pwzName, 0, (LPBYTE *)&pguiGroupData, MAX_PREFERRED_LENGTH, &dwRead, &dwTotal);
127 // Ignore these errors, and just go to the fallback checks
128 if (ERROR_BAD_NETPATH == er || ERROR_INVALID_NAME == er || NERR_UserNotFound == er)
129 {
130 Trace(REPORT_VERBOSE, "failed to get groups for user %ls from domain %ls with error code 0x%x - continuing", pwzName, (wz != NULL) ? wz : L"", HRESULT_FROM_WIN32(er));
131 er = ERROR_SUCCESS;
132 }
133 UserExitOnWin32Error(er, hr, "Failed to get list of global groups for user while checking group membership information for user: %ls", pwzName);
134
135 if (dwRead != dwTotal)
136 {
137 hr = HRESULT_FROM_WIN32(ERROR_MORE_DATA);
138 UserExitOnRootFailure(hr, "Failed to get entire list of groups (global) for user while checking group membership information for user: %ls", pwzName);
139 }
140
141 if (CheckIsMemberHelper(wzGroupUserDomain, pguiGroupData, dwRead))
142 {
143 *lpfMember = TRUE;
144 ExitFunction1(hr = S_OK);
145 }
146
147 if (NULL != pguiGroupData)
148 {
149 ::NetApiBufferFree(pguiGroupData);
150 pguiGroupData = NULL;
151 }
152
153 // If we fail with the global groups, try again with the local groups
154 er = ::NetUserGetLocalGroups(NULL, wzUserDomain, 0, LG_INCLUDE_INDIRECT, (LPBYTE *)&pguiGroupData, MAX_PREFERRED_LENGTH, &dwRead, &dwTotal);
155 // Ignore these errors, and just go to the fallback checks
156 if (NERR_UserNotFound == er || NERR_DCNotFound == er || RPC_S_SERVER_UNAVAILABLE == er)
157 {
158 Trace(REPORT_VERBOSE, "failed to get local groups for user %ls from domain %ls with error code 0x%x - continuing", pwzName, (wz != NULL) ? wz : L"", HRESULT_FROM_WIN32(er));
159 er = ERROR_SUCCESS;
160 }
161 UserExitOnWin32Error(er, hr, "Failed to get list of groups for user while checking group membership information for user: %ls", pwzName);
162
163 if (dwRead != dwTotal)
164 {
165 hr = HRESULT_FROM_WIN32(ERROR_MORE_DATA);
166 UserExitOnRootFailure(hr, "Failed to get entire list of groups (local) for user while checking group membership information for user: %ls", pwzName);
167 }
168
169 if (CheckIsMemberHelper(wzGroupUserDomain, pguiGroupData, dwRead))
170 {
171 *lpfMember = TRUE;
172 ExitFunction1(hr = S_OK);
173 }
174
175 // If the above methods failed, let's try active directory
176 hr = UserCreateADsPath(pwzDomain, pwzName, &bstrUser);
177 UserExitOnFailure(hr, "failed to create user ADsPath in order to check group membership for group: %ls domain: %ls", pwzName, pwzDomain);
178
179 hr = UserCreateADsPath(pwzGroupDomain, pwzGroupName, &bstrGroup);
180 UserExitOnFailure(hr, "failed to create group ADsPath in order to check group membership for group: %ls domain: %ls", pwzGroupName, pwzGroupDomain);
181
182 if (lstrlenW(pwzGroupDomain) > 0)
183 {
184 hr = ::ADsGetObject(bstrGroup, IID_IADsGroup, reinterpret_cast<void**>(&pGroup));
185 UserExitOnFailure(hr, "Failed to get group '%ls' from active directory.", reinterpret_cast<WCHAR*>(bstrGroup) );
186
187 hr = pGroup->IsMember(bstrUser, &vtBoolResult);
188 UserExitOnFailure(hr, "Failed to check if user %ls is a member of group '%ls' using active directory.", reinterpret_cast<WCHAR*>(bstrUser), reinterpret_cast<WCHAR*>(bstrGroup) );
189 }
190
191 if (vtBoolResult)
192 {
193 *lpfMember = TRUE;
194 ExitFunction1(hr = S_OK);
195 }
196
197 hr = ::ADsGetObject(bstrGroup, IID_IADsGroup, reinterpret_cast<void**>(&pGroup));
198 UserExitOnFailure(hr, "Failed to get group '%ls' from active directory.", reinterpret_cast<WCHAR*>(bstrGroup) );
199
200 hr = pGroup->IsMember(bstrUser, &vtBoolResult);
201 UserExitOnFailure(hr, "Failed to check if user %ls is a member of group '%ls' using active directory.", reinterpret_cast<WCHAR*>(bstrUser), reinterpret_cast<WCHAR*>(bstrGroup) );
202
203 if (vtBoolResult)
204 {
205 *lpfMember = TRUE;
206 ExitFunction1(hr = S_OK);
207 }
208
209LExit:
210 ReleaseObject(pGroup);
211 ReleaseBSTR(bstrUser);
212 ReleaseBSTR(bstrGroup);
213
214 if (NULL != pguiGroupData)
215 {
216 ::NetApiBufferFree(pguiGroupData);
217 }
218
219 return hr;
220}
221
222
223/*******************************************************************
224 Takes a domain and name, and allocates a BSTR which represents
225 DOMAIN\NAME's active directory path. The BSTR this function returns
226 should be released manually using the ReleaseBSTR() macro.
227********************************************************************/
228extern "C" HRESULT DAPI UserCreateADsPath(
229 __in_z LPCWSTR wzObjectDomain,
230 __in_z LPCWSTR wzObjectName,
231 __out BSTR *pbstrAdsPath
232 )
233{
234 Assert(wzObjectDomain && wzObjectName && *wzObjectName);
235
236 HRESULT hr = S_OK;
237 LPWSTR pwzAdsPath = NULL;
238
239 hr = StrAllocString(&pwzAdsPath, L"WinNT://", 0);
240 UserExitOnFailure(hr, "failed to allocate AdsPath string");
241
242 if (*wzObjectDomain)
243 {
244 hr = StrAllocFormatted(&pwzAdsPath, L"%s/%s", wzObjectDomain, wzObjectName);
245 UserExitOnFailure(hr, "failed to allocate AdsPath string");
246 }
247 else if (NULL != wcsstr(wzObjectName, L"\\") || NULL != wcsstr(wzObjectName, L"/"))
248 {
249 hr = StrAllocConcat(&pwzAdsPath, wzObjectName, 0);
250 UserExitOnFailure(hr, "failed to concat objectname: %ls", wzObjectName);
251 }
252 else
253 {
254 hr = StrAllocConcat(&pwzAdsPath, L"Localhost/", 0);
255 UserExitOnFailure(hr, "failed to concat LocalHost/");
256
257 hr = StrAllocConcat(&pwzAdsPath, wzObjectName, 0);
258 UserExitOnFailure(hr, "failed to concat object name: %ls", wzObjectName);
259 }
260
261 *pbstrAdsPath = ::SysAllocString(pwzAdsPath);
262 if (NULL == *pbstrAdsPath)
263 {
264 hr = E_OUTOFMEMORY;
265 }
266
267LExit:
268 ReleaseStr(pwzAdsPath);
269
270 return hr;
271}
272
273
274/*******************************************************************
275 Helper function to check if pwzGroupUserDomain (in form of "domain\username" is
276 a member of a given LOCALGROUP_USERS_INFO_0 structure. Useful to pass in the
277 output from both NetUserGetGroups() and NetUserGetLocalGroups()
278********************************************************************/
279static BOOL CheckIsMemberHelper(
280 __in_z LPCWSTR pwzGroupUserDomain,
281 __in_ecount(cguiGroupData) const GROUP_USERS_INFO_0 *pguiGroupData,
282 __in DWORD cguiGroupData
283 )
284{
285 if (NULL == pguiGroupData)
286 {
287 return FALSE;
288 }
289
290 for (DWORD dwCounter = 0; dwCounter < cguiGroupData; ++dwCounter)
291 {
292 // If the user is a member of the group, set the output flag to true
293 if (0 == lstrcmpiW(pwzGroupUserDomain, pguiGroupData[dwCounter].grui0_name))
294 {
295 return TRUE;
296 }
297 }
298
299 return FALSE;
300}
diff --git a/src/libs/dutil/WixToolset.DUtil/verutil.cpp b/src/libs/dutil/WixToolset.DUtil/verutil.cpp
new file mode 100644
index 00000000..21626f94
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/verutil.cpp
@@ -0,0 +1,647 @@
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// Exit macros
6#define VerExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__)
7#define VerExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__)
8#define VerExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__)
9#define VerExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__)
10#define VerExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__)
11#define VerExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__)
12#define VerExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_VERUTIL, p, x, e, s, __VA_ARGS__)
13#define VerExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_VERUTIL, p, x, s, __VA_ARGS__)
14#define VerExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_VERUTIL, p, x, e, s, __VA_ARGS__)
15#define VerExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_VERUTIL, p, x, s, __VA_ARGS__)
16#define VerExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_VERUTIL, e, x, s, __VA_ARGS__)
17
18// constants
19const DWORD GROW_RELEASE_LABELS = 3;
20
21// Forward declarations.
22static int CompareDword(
23 __in const DWORD& dw1,
24 __in const DWORD& dw2
25 );
26static HRESULT CompareReleaseLabel(
27 __in const VERUTIL_VERSION_RELEASE_LABEL* p1,
28 __in LPCWSTR wzVersion1,
29 __in const VERUTIL_VERSION_RELEASE_LABEL* p2,
30 __in LPCWSTR wzVersion2,
31 __out int* pnResult
32 );
33static HRESULT CompareVersionSubstring(
34 __in LPCWSTR wzString1,
35 __in int cchCount1,
36 __in LPCWSTR wzString2,
37 __in int cchCount2,
38 __out int* pnResult
39 );
40
41
42DAPI_(HRESULT) VerCompareParsedVersions(
43 __in_opt VERUTIL_VERSION* pVersion1,
44 __in_opt VERUTIL_VERSION* pVersion2,
45 __out int* pnResult
46 )
47{
48 HRESULT hr = S_OK;
49 int nResult = 0;
50 DWORD cMaxReleaseLabels = 0;
51 BOOL fCompareMetadata = FALSE;
52
53 if (pVersion1 && !pVersion1->sczVersion ||
54 pVersion2 && !pVersion2->sczVersion)
55 {
56 ExitFunction1(hr = E_INVALIDARG);
57 }
58
59 if (pVersion1 == pVersion2)
60 {
61 ExitFunction1(nResult = 0);
62 }
63 else if (pVersion1 && !pVersion2)
64 {
65 ExitFunction1(nResult = 1);
66 }
67 else if (!pVersion1 && pVersion2)
68 {
69 ExitFunction1(nResult = -1);
70 }
71
72 nResult = CompareDword(pVersion1->dwMajor, pVersion2->dwMajor);
73 if (0 != nResult)
74 {
75 ExitFunction();
76 }
77
78 nResult = CompareDword(pVersion1->dwMinor, pVersion2->dwMinor);
79 if (0 != nResult)
80 {
81 ExitFunction();
82 }
83
84 nResult = CompareDword(pVersion1->dwPatch, pVersion2->dwPatch);
85 if (0 != nResult)
86 {
87 ExitFunction();
88 }
89
90 nResult = CompareDword(pVersion1->dwRevision, pVersion2->dwRevision);
91 if (0 != nResult)
92 {
93 ExitFunction();
94 }
95
96 if (pVersion1->cReleaseLabels)
97 {
98 if (pVersion2->cReleaseLabels)
99 {
100 cMaxReleaseLabels = max(pVersion1->cReleaseLabels, pVersion2->cReleaseLabels);
101 }
102 else
103 {
104 ExitFunction1(nResult = -1);
105 }
106 }
107 else if (pVersion2->cReleaseLabels)
108 {
109 ExitFunction1(nResult = 1);
110 }
111
112 if (cMaxReleaseLabels)
113 {
114 for (DWORD i = 0; i < cMaxReleaseLabels; ++i)
115 {
116 VERUTIL_VERSION_RELEASE_LABEL* pReleaseLabel1 = pVersion1->cReleaseLabels > i ? pVersion1->rgReleaseLabels + i : NULL;
117 VERUTIL_VERSION_RELEASE_LABEL* pReleaseLabel2 = pVersion2->cReleaseLabels > i ? pVersion2->rgReleaseLabels + i : NULL;
118
119 hr = CompareReleaseLabel(pReleaseLabel1, pVersion1->sczVersion, pReleaseLabel2, pVersion2->sczVersion, &nResult);
120 if (FAILED(hr) || 0 != nResult)
121 {
122 ExitFunction();
123 }
124 }
125 }
126
127 if (pVersion1->fInvalid)
128 {
129 if (!pVersion2->fInvalid)
130 {
131 ExitFunction1(nResult = -1);
132 }
133 else
134 {
135 fCompareMetadata = TRUE;
136 }
137 }
138 else if (pVersion2->fInvalid)
139 {
140 ExitFunction1(nResult = 1);
141 }
142
143 if (fCompareMetadata)
144 {
145 hr = CompareVersionSubstring(pVersion1->sczVersion + pVersion1->cchMetadataOffset, -1, pVersion2->sczVersion + pVersion2->cchMetadataOffset, -1, &nResult);
146 }
147
148LExit:
149 *pnResult = nResult;
150 return hr;
151}
152
153DAPI_(HRESULT) VerCompareStringVersions(
154 __in_z LPCWSTR wzVersion1,
155 __in_z LPCWSTR wzVersion2,
156 __in BOOL fStrict,
157 __out int* pnResult
158 )
159{
160 HRESULT hr = S_OK;
161 VERUTIL_VERSION* pVersion1 = NULL;
162 VERUTIL_VERSION* pVersion2 = NULL;
163 int nResult = 0;
164
165 hr = VerParseVersion(wzVersion1, 0, fStrict, &pVersion1);
166 VerExitOnFailure(hr, "Failed to parse Verutil version '%ls'", wzVersion1);
167
168 hr = VerParseVersion(wzVersion2, 0, fStrict, &pVersion2);
169 VerExitOnFailure(hr, "Failed to parse Verutil version '%ls'", wzVersion2);
170
171 hr = VerCompareParsedVersions(pVersion1, pVersion2, &nResult);
172 VerExitOnFailure(hr, "Failed to compare parsed Verutil versions '%ls' and '%ls'.", wzVersion1, wzVersion2);
173
174LExit:
175 *pnResult = nResult;
176
177 ReleaseVerutilVersion(pVersion1);
178 ReleaseVerutilVersion(pVersion2);
179
180 return hr;
181}
182
183DAPI_(HRESULT) VerCopyVersion(
184 __in VERUTIL_VERSION* pSource,
185 __out VERUTIL_VERSION** ppVersion
186 )
187{
188 HRESULT hr = S_OK;
189 VERUTIL_VERSION* pCopy = NULL;
190
191 pCopy = reinterpret_cast<VERUTIL_VERSION*>(MemAlloc(sizeof(VERUTIL_VERSION), TRUE));
192 VerExitOnNull(pCopy, hr, E_OUTOFMEMORY, "Failed to allocate memory for Verutil version copy.");
193
194 hr = StrAllocString(&pCopy->sczVersion, pSource->sczVersion, 0);
195 VerExitOnFailure(hr, "Failed to copy Verutil version string '%ls'.", pSource->sczVersion);
196
197 pCopy->dwMajor = pSource->dwMajor;
198 pCopy->dwMinor = pSource->dwMinor;
199 pCopy->dwPatch = pSource->dwPatch;
200 pCopy->dwRevision = pSource->dwRevision;
201
202 if (pSource->cReleaseLabels)
203 {
204 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pCopy->rgReleaseLabels), 0, sizeof(VERUTIL_VERSION_RELEASE_LABEL), pSource->cReleaseLabels);
205 VerExitOnFailure(hr, "Failed to allocate memory for Verutil version release labels copies.");
206
207 pCopy->cReleaseLabels = pSource->cReleaseLabels;
208
209 for (DWORD i = 0; i < pCopy->cReleaseLabels; ++i)
210 {
211 VERUTIL_VERSION_RELEASE_LABEL* pSourceLabel = pSource->rgReleaseLabels + i;
212 VERUTIL_VERSION_RELEASE_LABEL* pCopyLabel = pCopy->rgReleaseLabels + i;
213
214 pCopyLabel->cchLabelOffset = pSourceLabel->cchLabelOffset;
215 pCopyLabel->cchLabel = pSourceLabel->cchLabel;
216 pCopyLabel->fNumeric = pSourceLabel->fNumeric;
217 pCopyLabel->dwValue = pSourceLabel->dwValue;
218 }
219 }
220
221 pCopy->cchMetadataOffset = pSource->cchMetadataOffset;
222 pCopy->fInvalid = pSource->fInvalid;
223
224 *ppVersion = pCopy;
225 pCopy = NULL;
226
227LExit:
228 ReleaseVerutilVersion(pCopy);
229
230 return hr;
231}
232
233DAPI_(void) VerFreeVersion(
234 __in VERUTIL_VERSION* pVersion
235 )
236{
237 if (pVersion)
238 {
239 ReleaseStr(pVersion->sczVersion);
240 ReleaseMem(pVersion->rgReleaseLabels);
241 ReleaseMem(pVersion);
242 }
243}
244
245DAPI_(HRESULT) VerParseVersion(
246 __in_z LPCWSTR wzVersion,
247 __in SIZE_T cchVersion,
248 __in BOOL fStrict,
249 __out VERUTIL_VERSION** ppVersion
250 )
251{
252 HRESULT hr = S_OK;
253 VERUTIL_VERSION* pVersion = NULL;
254 LPCWSTR wzEnd = NULL;
255 LPCWSTR wzPartBegin = NULL;
256 LPCWSTR wzPartEnd = NULL;
257 BOOL fInvalid = FALSE;
258 BOOL fLastPart = FALSE;
259 BOOL fTrailingDot = FALSE;
260 BOOL fParsedVersionNumber = FALSE;
261 BOOL fExpectedReleaseLabels = FALSE;
262 DWORD iPart = 0;
263
264 if (!wzVersion || !ppVersion)
265 {
266 ExitFunction1(hr = E_INVALIDARG);
267 }
268
269 // Get string length if not provided.
270 if (!cchVersion)
271 {
272 hr = ::StringCchLengthW(wzVersion, STRSAFE_MAX_CCH, reinterpret_cast<size_t*>(&cchVersion));
273 VerExitOnRootFailure(hr, "Failed to get length of version string: %ls", wzVersion);
274 }
275 else if (INT_MAX < cchVersion)
276 {
277 VerExitOnRootFailure(hr = E_INVALIDARG, "Version string is too long: %Iu", cchVersion);
278 }
279
280 if (L'v' == *wzVersion || L'V' == *wzVersion)
281 {
282 ++wzVersion;
283 --cchVersion;
284 }
285
286 pVersion = reinterpret_cast<VERUTIL_VERSION*>(MemAlloc(sizeof(VERUTIL_VERSION), TRUE));
287 VerExitOnNull(pVersion, hr, E_OUTOFMEMORY, "Failed to allocate memory for Verutil version '%ls'.", wzVersion);
288
289 hr = StrAllocString(&pVersion->sczVersion, wzVersion, cchVersion);
290 VerExitOnFailure(hr, "Failed to copy Verutil version string '%ls'.", wzVersion);
291
292 wzVersion = wzPartBegin = wzPartEnd = pVersion->sczVersion;
293
294 // Save end pointer.
295 wzEnd = wzVersion + cchVersion;
296
297 // Parse version number
298 while (wzPartBegin < wzEnd)
299 {
300 fTrailingDot = FALSE;
301
302 // Find end of part.
303 for (;;)
304 {
305 if (wzPartEnd >= wzEnd)
306 {
307 fLastPart = TRUE;
308 break;
309 }
310
311 switch (*wzPartEnd)
312 {
313 case L'0':
314 case L'1':
315 case L'2':
316 case L'3':
317 case L'4':
318 case L'5':
319 case L'6':
320 case L'7':
321 case L'8':
322 case L'9':
323 ++wzPartEnd;
324 continue;
325 case L'.':
326 fTrailingDot = TRUE;
327 break;
328 case L'-':
329 case L'+':
330 fLastPart = TRUE;
331 break;
332 default:
333 fInvalid = TRUE;
334 break;
335 }
336
337 break;
338 }
339
340 if (wzPartBegin == wzPartEnd)
341 {
342 fInvalid = TRUE;
343 }
344
345 if (fInvalid)
346 {
347 break;
348 }
349
350 DWORD cchPart = 0;
351 hr = ::PtrdiffTToDWord(wzPartEnd - wzPartBegin, &cchPart);
352 if (FAILED(hr))
353 {
354 fInvalid = TRUE;
355 break;
356 }
357
358 // Parse version part.
359 UINT uPart = 0;
360 hr = StrStringToUInt32(wzPartBegin, cchPart, &uPart);
361 if (FAILED(hr))
362 {
363 fInvalid = TRUE;
364 break;
365 }
366
367 switch (iPart)
368 {
369 case 0:
370 pVersion->dwMajor = uPart;
371 break;
372 case 1:
373 pVersion->dwMinor = uPart;
374 break;
375 case 2:
376 pVersion->dwPatch = uPart;
377 break;
378 case 3:
379 pVersion->dwRevision = uPart;
380 break;
381 }
382
383 if (fTrailingDot)
384 {
385 ++wzPartEnd;
386 }
387 wzPartBegin = wzPartEnd;
388 ++iPart;
389
390 if (4 <= iPart || fLastPart)
391 {
392 fParsedVersionNumber = TRUE;
393 break;
394 }
395 }
396
397 fInvalid |= !fParsedVersionNumber || fTrailingDot;
398
399 if (!fInvalid && wzPartBegin < wzEnd && *wzPartBegin == L'-')
400 {
401 wzPartBegin = wzPartEnd = wzPartBegin + 1;
402 fExpectedReleaseLabels = TRUE;
403 fLastPart = FALSE;
404 }
405
406 while (fExpectedReleaseLabels && wzPartBegin < wzEnd)
407 {
408 fTrailingDot = FALSE;
409
410 // Find end of part.
411 for (;;)
412 {
413 if (wzPartEnd >= wzEnd)
414 {
415 fLastPart = TRUE;
416 break;
417 }
418
419 if (*wzPartEnd >= L'0' && *wzPartEnd <= L'9' ||
420 *wzPartEnd >= L'A' && *wzPartEnd <= L'Z' ||
421 *wzPartEnd >= L'a' && *wzPartEnd <= L'z' ||
422 *wzPartEnd == L'-')
423 {
424 ++wzPartEnd;
425 continue;
426 }
427 else if (*wzPartEnd == L'+')
428 {
429 fLastPart = TRUE;
430 }
431 else if (*wzPartEnd == L'.')
432 {
433 fTrailingDot = TRUE;
434 }
435 else
436 {
437 fInvalid = TRUE;
438 }
439
440 break;
441 }
442
443 if (wzPartBegin == wzPartEnd)
444 {
445 fInvalid = TRUE;
446 }
447
448 if (fInvalid)
449 {
450 break;
451 }
452
453 int cchLabel = 0;
454 hr = ::PtrdiffTToInt32(wzPartEnd - wzPartBegin, &cchLabel);
455 if (FAILED(hr) || 0 > cchLabel)
456 {
457 fInvalid = TRUE;
458 break;
459 }
460
461 hr = MemReAllocArray(reinterpret_cast<LPVOID*>(&pVersion->rgReleaseLabels), pVersion->cReleaseLabels, sizeof(VERUTIL_VERSION_RELEASE_LABEL), GROW_RELEASE_LABELS - (pVersion->cReleaseLabels % GROW_RELEASE_LABELS));
462 VerExitOnFailure(hr, "Failed to allocate memory for Verutil version release labels '%ls'", wzVersion);
463
464 VERUTIL_VERSION_RELEASE_LABEL* pReleaseLabel = pVersion->rgReleaseLabels + pVersion->cReleaseLabels;
465 ++pVersion->cReleaseLabels;
466
467 // Try to parse as number.
468 UINT uLabel = 0;
469 hr = StrStringToUInt32(wzPartBegin, cchLabel, &uLabel);
470 if (SUCCEEDED(hr))
471 {
472 pReleaseLabel->fNumeric = TRUE;
473 pReleaseLabel->dwValue = uLabel;
474 }
475
476 pReleaseLabel->cchLabelOffset = wzPartBegin - pVersion->sczVersion;
477 pReleaseLabel->cchLabel = cchLabel;
478
479 if (fTrailingDot)
480 {
481 ++wzPartEnd;
482 }
483 wzPartBegin = wzPartEnd;
484
485 if (fLastPart)
486 {
487 break;
488 }
489 }
490
491 fInvalid |= fExpectedReleaseLabels && (!pVersion->cReleaseLabels || fTrailingDot);
492
493 if (!fInvalid && wzPartBegin < wzEnd)
494 {
495 if (*wzPartBegin == L'+')
496 {
497 wzPartBegin = wzPartEnd = wzPartBegin + 1;
498 }
499 else
500 {
501 fInvalid = TRUE;
502 }
503 }
504
505 if (fInvalid && fStrict)
506 {
507 ExitFunction1(hr = E_INVALIDARG);
508 }
509
510 pVersion->cchMetadataOffset = min(wzPartBegin, wzEnd) - pVersion->sczVersion;
511 pVersion->fInvalid = fInvalid;
512
513 *ppVersion = pVersion;
514 pVersion = NULL;
515 hr = S_OK;
516
517LExit:
518 ReleaseVerutilVersion(pVersion);
519
520 return hr;
521}
522
523DAPI_(HRESULT) VerVersionFromQword(
524 __in DWORD64 qwVersion,
525 __out VERUTIL_VERSION** ppVersion
526 )
527{
528 HRESULT hr = S_OK;
529 VERUTIL_VERSION* pVersion = NULL;
530
531 pVersion = reinterpret_cast<VERUTIL_VERSION*>(MemAlloc(sizeof(VERUTIL_VERSION), TRUE));
532 VerExitOnNull(pVersion, hr, E_OUTOFMEMORY, "Failed to allocate memory for Verutil version from QWORD.");
533
534 pVersion->dwMajor = (WORD)(qwVersion >> 48 & 0xffff);
535 pVersion->dwMinor = (WORD)(qwVersion >> 32 & 0xffff);
536 pVersion->dwPatch = (WORD)(qwVersion >> 16 & 0xffff);
537 pVersion->dwRevision = (WORD)(qwVersion & 0xffff);
538
539 hr = StrAllocFormatted(&pVersion->sczVersion, L"%lu.%lu.%lu.%lu", pVersion->dwMajor, pVersion->dwMinor, pVersion->dwPatch, pVersion->dwRevision);
540 ExitOnFailure(hr, "Failed to allocate and format the version string.");
541
542 pVersion->cchMetadataOffset = lstrlenW(pVersion->sczVersion);
543
544 *ppVersion = pVersion;
545 pVersion = NULL;
546
547LExit:
548 ReleaseVerutilVersion(pVersion);
549
550 return hr;
551}
552
553
554static int CompareDword(
555 __in const DWORD& dw1,
556 __in const DWORD& dw2
557 )
558{
559 int nResult = 0;
560
561 if (dw1 > dw2)
562 {
563 nResult = 1;
564 }
565 else if (dw1 < dw2)
566 {
567 nResult = -1;
568 }
569
570 return nResult;
571}
572
573static HRESULT CompareReleaseLabel(
574 __in const VERUTIL_VERSION_RELEASE_LABEL* p1,
575 __in LPCWSTR wzVersion1,
576 __in const VERUTIL_VERSION_RELEASE_LABEL* p2,
577 __in LPCWSTR wzVersion2,
578 __out int* pnResult
579 )
580{
581 HRESULT hr = S_OK;
582 int nResult = 0;
583
584 if (p1 == p2)
585 {
586 ExitFunction();
587 }
588 else if (p1 && !p2)
589 {
590 ExitFunction1(nResult = 1);
591 }
592 else if (!p1 && p2)
593 {
594 ExitFunction1(nResult = -1);
595 }
596
597 if (p1->fNumeric)
598 {
599 if (p2->fNumeric)
600 {
601 nResult = CompareDword(p1->dwValue, p2->dwValue);
602 }
603 else
604 {
605 nResult = -1;
606 }
607 }
608 else
609 {
610 if (p2->fNumeric)
611 {
612 nResult = 1;
613 }
614 else
615 {
616 hr = CompareVersionSubstring(wzVersion1 + p1->cchLabelOffset, p1->cchLabel, wzVersion2 + p2->cchLabelOffset, p2->cchLabel, &nResult);
617 }
618 }
619
620LExit:
621 *pnResult = nResult;
622
623 return hr;
624}
625
626static HRESULT CompareVersionSubstring(
627 __in LPCWSTR wzString1,
628 __in int cchCount1,
629 __in LPCWSTR wzString2,
630 __in int cchCount2,
631 __out int* pnResult
632 )
633{
634 HRESULT hr = S_OK;
635 int nResult = 0;
636
637 nResult = ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, wzString1, cchCount1, wzString2, cchCount2);
638 if (!nResult)
639 {
640 VerExitOnLastError(hr, "Failed to compare version substrings");
641 }
642
643LExit:
644 *pnResult = nResult - 2;
645
646 return hr;
647}
diff --git a/src/libs/dutil/WixToolset.DUtil/wiutil.cpp b/src/libs/dutil/WixToolset.DUtil/wiutil.cpp
new file mode 100644
index 00000000..7414ac42
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/wiutil.cpp
@@ -0,0 +1,1629 @@
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// Exit macros
7#define WiuExitTrace(x, s, ...) ExitTraceSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__)
8#define WiuExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__)
9#define WiuExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__)
10#define WiuExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__)
11#define WiuExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__)
12#define WiuExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__)
13#define WiuExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__)
14#define WiuExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_WIUTIL, p, x, e, s, __VA_ARGS__)
15#define WiuExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_WIUTIL, p, x, s, __VA_ARGS__)
16#define WiuExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_WIUTIL, p, x, e, s, __VA_ARGS__)
17#define WiuExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_WIUTIL, p, x, s, __VA_ARGS__)
18#define WiuExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_WIUTIL, e, x, s, __VA_ARGS__)
19#define WiuExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_WIUTIL, g, x, s, __VA_ARGS__)
20
21
22// constants
23
24const DWORD WIU_MSI_PROGRESS_INVALID = 0xFFFFFFFF;
25const DWORD WIU_GOOD_ENOUGH_PROPERTY_LENGTH = 64;
26
27
28// structs
29
30
31static PFN_MSIENABLELOGW vpfnMsiEnableLogW = ::MsiEnableLogW;
32static PFN_MSIGETPRODUCTINFOW vpfnMsiGetProductInfoW = ::MsiGetProductInfoW;
33static PFN_MSIQUERYFEATURESTATEW vpfnMsiQueryFeatureStateW = ::MsiQueryFeatureStateW;
34static PFN_MSIGETCOMPONENTPATHW vpfnMsiGetComponentPathW = ::MsiGetComponentPathW;
35static PFN_MSILOCATECOMPONENTW vpfnMsiLocateComponentW = ::MsiLocateComponentW;
36static PFN_MSIINSTALLPRODUCTW vpfnMsiInstallProductW = ::MsiInstallProductW;
37static PFN_MSICONFIGUREPRODUCTEXW vpfnMsiConfigureProductExW = ::MsiConfigureProductExW;
38static PFN_MSIREMOVEPATCHESW vpfnMsiRemovePatchesW = ::MsiRemovePatchesW;
39static PFN_MSISETINTERNALUI vpfnMsiSetInternalUI = ::MsiSetInternalUI;
40static PFN_MSISETEXTERNALUIW vpfnMsiSetExternalUIW = ::MsiSetExternalUIW;
41static PFN_MSIENUMPRODUCTSW vpfnMsiEnumProductsW = ::MsiEnumProductsW;
42static PFN_MSIENUMRELATEDPRODUCTSW vpfnMsiEnumRelatedProductsW = ::MsiEnumRelatedProductsW;
43
44// MSI 3.0+
45static PFN_MSIDETERMINEPATCHSEQUENCEW vpfnMsiDeterminePatchSequenceW = NULL;
46static PFN_MSIDETERMINEAPPLICABLEPATCHESW vpfnMsiDetermineApplicablePatchesW = NULL;
47static PFN_MSIENUMPRODUCTSEXW vpfnMsiEnumProductsExW = NULL;
48static PFN_MSIGETPATCHINFOEXW vpfnMsiGetPatchInfoExW = NULL;
49static PFN_MSIGETPRODUCTINFOEXW vpfnMsiGetProductInfoExW = NULL;
50static PFN_MSISETEXTERNALUIRECORD vpfnMsiSetExternalUIRecord = NULL;
51static PFN_MSISOURCELISTADDSOURCEEXW vpfnMsiSourceListAddSourceExW = NULL;
52
53static HMODULE vhMsiDll = NULL;
54static PFN_MSIDETERMINEPATCHSEQUENCEW vpfnMsiDeterminePatchSequenceWFromLibrary = NULL;
55static PFN_MSIDETERMINEAPPLICABLEPATCHESW vpfnMsiDetermineApplicablePatchesWFromLibrary = NULL;
56static PFN_MSIENUMPRODUCTSEXW vpfnMsiEnumProductsExWFromLibrary = NULL;
57static PFN_MSIGETPATCHINFOEXW vpfnMsiGetPatchInfoExWFromLibrary = NULL;
58static PFN_MSIGETPRODUCTINFOEXW vpfnMsiGetProductInfoExWFromLibrary = NULL;
59static PFN_MSISETEXTERNALUIRECORD vpfnMsiSetExternalUIRecordFromLibrary = NULL;
60static PFN_MSISOURCELISTADDSOURCEEXW vpfnMsiSourceListAddSourceExWFromLibrary = NULL;
61
62// MSI Transactions v4.5+
63static PFN_MSIBEGINTRANSACTIONW vpfnMsiBeginTransaction = NULL;
64static PFN_MSIENDTRANSACTION vpfnMsiEndTransaction = NULL;
65
66static BOOL vfWiuInitialized = FALSE;
67
68// globals
69static DWORD vdwMsiDllMajorMinor = 0;
70static DWORD vdwMsiDllBuildRevision = 0;
71
72
73// internal function declarations
74
75static DWORD CheckForRestartErrorCode(
76 __in DWORD dwErrorCode,
77 __out WIU_RESTART* pRestart
78 );
79static INT CALLBACK InstallEngineCallback(
80 __in LPVOID pvContext,
81 __in UINT uiMessage,
82 __in_z_opt LPCWSTR wzMessage
83 );
84static INT CALLBACK InstallEngineRecordCallback(
85 __in LPVOID pvContext,
86 __in UINT uiMessage,
87 __in_opt MSIHANDLE hRecord
88 );
89static INT HandleInstallMessage(
90 __in WIU_MSI_EXECUTE_CONTEXT* pContext,
91 __in INSTALLMESSAGE mt,
92 __in UINT uiFlags,
93 __in_z LPCWSTR wzMessage,
94 __in_opt MSIHANDLE hRecord
95 );
96static INT HandleInstallProgress(
97 __in WIU_MSI_EXECUTE_CONTEXT* pContext,
98 __in_z_opt LPCWSTR wzMessage,
99 __in_opt MSIHANDLE hRecord
100 );
101static INT SendMsiMessage(
102 __in WIU_MSI_EXECUTE_CONTEXT* pContext,
103 __in INSTALLMESSAGE mt,
104 __in UINT uiFlags,
105 __in_z LPCWSTR wzMessage,
106 __in_opt MSIHANDLE hRecord
107 );
108static INT SendErrorMessage(
109 __in WIU_MSI_EXECUTE_CONTEXT* pContext,
110 __in UINT uiFlags,
111 __in_z LPCWSTR wzMessage,
112 __in_opt MSIHANDLE hRecord
113 );
114static INT SendFilesInUseMessage(
115 __in WIU_MSI_EXECUTE_CONTEXT* pContext,
116 __in_opt MSIHANDLE hRecord,
117 __in BOOL fRestartManagerRequest
118 );
119static INT SendProgressUpdate(
120 __in WIU_MSI_EXECUTE_CONTEXT* pContext
121 );
122static void ResetProgress(
123 __in WIU_MSI_EXECUTE_CONTEXT* pContext
124 );
125static DWORD CalculatePhaseProgress(
126 __in WIU_MSI_EXECUTE_CONTEXT* pContext,
127 __in DWORD dwProgressIndex,
128 __in DWORD dwWeightPercentage
129 );
130void InitializeMessageData(
131 __in_opt MSIHANDLE hRecord,
132 __deref_out_ecount(*pcData) LPWSTR** prgsczData,
133 __out DWORD* pcData
134 );
135void UninitializeMessageData(
136 __in LPWSTR* rgsczData,
137 __in DWORD cData
138 );
139
140
141/********************************************************************
142 WiuInitialize - initializes wiutil
143
144*********************************************************************/
145extern "C" HRESULT DAPI WiuInitialize(
146 )
147{
148 HRESULT hr = S_OK;
149 LPWSTR sczMsiDllPath = NULL;
150
151 hr = LoadSystemLibraryWithPath(L"Msi.dll", &vhMsiDll, &sczMsiDllPath);
152 WiuExitOnFailure(hr, "Failed to load Msi.DLL");
153
154 // Ignore failures
155 FileVersion(sczMsiDllPath, &vdwMsiDllMajorMinor, &vdwMsiDllBuildRevision);
156
157 vpfnMsiDeterminePatchSequenceWFromLibrary = reinterpret_cast<PFN_MSIDETERMINEPATCHSEQUENCEW>(::GetProcAddress(vhMsiDll, "MsiDeterminePatchSequenceW"));
158 if (NULL == vpfnMsiDeterminePatchSequenceW)
159 {
160 vpfnMsiDeterminePatchSequenceW = vpfnMsiDeterminePatchSequenceWFromLibrary;
161 }
162
163 vpfnMsiDetermineApplicablePatchesWFromLibrary = reinterpret_cast<PFN_MSIDETERMINEAPPLICABLEPATCHESW>(::GetProcAddress(vhMsiDll, "MsiDetermineApplicablePatchesW"));
164 if (NULL == vpfnMsiDetermineApplicablePatchesW)
165 {
166 vpfnMsiDetermineApplicablePatchesW = vpfnMsiDetermineApplicablePatchesWFromLibrary;
167 }
168
169 vpfnMsiEnumProductsExWFromLibrary = reinterpret_cast<PFN_MSIENUMPRODUCTSEXW>(::GetProcAddress(vhMsiDll, "MsiEnumProductsExW"));
170 if (NULL == vpfnMsiEnumProductsExW)
171 {
172 vpfnMsiEnumProductsExW = vpfnMsiEnumProductsExWFromLibrary;
173 }
174
175 vpfnMsiGetPatchInfoExWFromLibrary = reinterpret_cast<PFN_MSIGETPATCHINFOEXW>(::GetProcAddress(vhMsiDll, "MsiGetPatchInfoExW"));
176 if (NULL == vpfnMsiGetPatchInfoExW)
177 {
178 vpfnMsiGetPatchInfoExW = vpfnMsiGetPatchInfoExWFromLibrary;
179 }
180
181 vpfnMsiGetProductInfoExWFromLibrary = reinterpret_cast<PFN_MSIGETPRODUCTINFOEXW>(::GetProcAddress(vhMsiDll, "MsiGetProductInfoExW"));
182 if (NULL == vpfnMsiGetProductInfoExW)
183 {
184 vpfnMsiGetProductInfoExW = vpfnMsiGetProductInfoExWFromLibrary;
185 }
186
187 vpfnMsiSetExternalUIRecordFromLibrary = reinterpret_cast<PFN_MSISETEXTERNALUIRECORD>(::GetProcAddress(vhMsiDll, "MsiSetExternalUIRecord"));
188 if (NULL == vpfnMsiSetExternalUIRecord)
189 {
190 vpfnMsiSetExternalUIRecord = vpfnMsiSetExternalUIRecordFromLibrary;
191 }
192
193 //static PFN_MSISOURCELISTADDSOURCEEXW vpfnMsiSourceListAddSourceExW = NULL;
194 vpfnMsiSourceListAddSourceExWFromLibrary = reinterpret_cast<PFN_MSISOURCELISTADDSOURCEEXW>(::GetProcAddress(vhMsiDll, "MsiSourceListAddSourceExW"));
195 if (NULL == vpfnMsiSourceListAddSourceExW)
196 {
197 vpfnMsiSourceListAddSourceExW = vpfnMsiSourceListAddSourceExWFromLibrary;
198 }
199
200 // MSI Transaction functions
201 if (NULL == vpfnMsiBeginTransaction)
202 {
203 vpfnMsiBeginTransaction = reinterpret_cast<PFN_MSIBEGINTRANSACTIONW>(::GetProcAddress(vhMsiDll, "MsiBeginTransactionW"));
204 }
205
206 if (NULL == vpfnMsiEndTransaction)
207 {
208 vpfnMsiEndTransaction = reinterpret_cast<PFN_MSIENDTRANSACTION>(::GetProcAddress(vhMsiDll, "MsiEndTransaction"));
209 }
210
211 vfWiuInitialized = TRUE;
212
213LExit:
214 ReleaseStr(sczMsiDllPath);
215 return hr;
216}
217
218
219/********************************************************************
220 WiuUninitialize - uninitializes wiutil
221
222*********************************************************************/
223extern "C" void DAPI WiuUninitialize(
224 )
225{
226 if (vhMsiDll)
227 {
228 ::FreeLibrary(vhMsiDll);
229 vhMsiDll = NULL;
230 vpfnMsiSetExternalUIRecordFromLibrary = NULL;
231 vpfnMsiGetProductInfoExWFromLibrary = NULL;
232 vpfnMsiGetPatchInfoExWFromLibrary = NULL;
233 vpfnMsiEnumProductsExWFromLibrary = NULL;
234 vpfnMsiDetermineApplicablePatchesWFromLibrary = NULL;
235 vpfnMsiDeterminePatchSequenceWFromLibrary = NULL;
236 vpfnMsiSourceListAddSourceExWFromLibrary = NULL;
237 vpfnMsiBeginTransaction = NULL;
238 vpfnMsiEndTransaction = NULL;
239 }
240
241 vfWiuInitialized = FALSE;
242}
243
244
245/********************************************************************
246 WiuFunctionOverride - overrides the Windows installer functions. Typically used
247 for unit testing.
248
249*********************************************************************/
250extern "C" void DAPI WiuFunctionOverride(
251 __in_opt PFN_MSIENABLELOGW pfnMsiEnableLogW,
252 __in_opt PFN_MSIGETCOMPONENTPATHW pfnMsiGetComponentPathW,
253 __in_opt PFN_MSILOCATECOMPONENTW pfnMsiLocateComponentW,
254 __in_opt PFN_MSIQUERYFEATURESTATEW pfnMsiQueryFeatureStateW,
255 __in_opt PFN_MSIGETPRODUCTINFOW pfnMsiGetProductInfoW,
256 __in_opt PFN_MSIGETPRODUCTINFOEXW pfnMsiGetProductInfoExW,
257 __in_opt PFN_MSIINSTALLPRODUCTW pfnMsiInstallProductW,
258 __in_opt PFN_MSICONFIGUREPRODUCTEXW pfnMsiConfigureProductExW,
259 __in_opt PFN_MSISETINTERNALUI pfnMsiSetInternalUI,
260 __in_opt PFN_MSISETEXTERNALUIW pfnMsiSetExternalUIW,
261 __in_opt PFN_MSIENUMRELATEDPRODUCTSW pfnMsiEnumRelatedProductsW,
262 __in_opt PFN_MSISETEXTERNALUIRECORD pfnMsiSetExternalUIRecord,
263 __in_opt PFN_MSISOURCELISTADDSOURCEEXW pfnMsiSourceListAddSourceExW
264 )
265{
266 vpfnMsiEnableLogW = pfnMsiEnableLogW ? pfnMsiEnableLogW : ::MsiEnableLogW;
267 vpfnMsiGetComponentPathW = pfnMsiGetComponentPathW ? pfnMsiGetComponentPathW : ::MsiGetComponentPathW;
268 vpfnMsiLocateComponentW = pfnMsiLocateComponentW ? pfnMsiLocateComponentW : ::MsiLocateComponentW;
269 vpfnMsiQueryFeatureStateW = pfnMsiQueryFeatureStateW ? pfnMsiQueryFeatureStateW : ::MsiQueryFeatureStateW;
270 vpfnMsiGetProductInfoW = pfnMsiGetProductInfoW ? pfnMsiGetProductInfoW : vpfnMsiGetProductInfoW;
271 vpfnMsiInstallProductW = pfnMsiInstallProductW ? pfnMsiInstallProductW : ::MsiInstallProductW;
272 vpfnMsiConfigureProductExW = pfnMsiConfigureProductExW ? pfnMsiConfigureProductExW : ::MsiConfigureProductExW;
273 vpfnMsiSetInternalUI = pfnMsiSetInternalUI ? pfnMsiSetInternalUI : ::MsiSetInternalUI;
274 vpfnMsiSetExternalUIW = pfnMsiSetExternalUIW ? pfnMsiSetExternalUIW : ::MsiSetExternalUIW;
275 vpfnMsiEnumRelatedProductsW = pfnMsiEnumRelatedProductsW ? pfnMsiEnumRelatedProductsW : ::MsiEnumRelatedProductsW;
276 vpfnMsiGetProductInfoExW = pfnMsiGetProductInfoExW ? pfnMsiGetProductInfoExW : vpfnMsiGetProductInfoExWFromLibrary;
277 vpfnMsiSetExternalUIRecord = pfnMsiSetExternalUIRecord ? pfnMsiSetExternalUIRecord : vpfnMsiSetExternalUIRecordFromLibrary;
278 vpfnMsiSourceListAddSourceExW = pfnMsiSourceListAddSourceExW ? pfnMsiSourceListAddSourceExW : vpfnMsiSourceListAddSourceExWFromLibrary;
279}
280
281
282extern "C" HRESULT DAPI WiuGetComponentPath(
283 __in_z LPCWSTR wzProductCode,
284 __in_z LPCWSTR wzComponentId,
285 __out INSTALLSTATE* pInstallState,
286 __out_z LPWSTR* psczValue
287 )
288{
289 HRESULT hr = S_OK;
290 DWORD cch = WIU_GOOD_ENOUGH_PROPERTY_LENGTH;
291 DWORD cchCompare;
292
293 hr = StrAlloc(psczValue, cch);
294 WiuExitOnFailure(hr, "Failed to allocate string for component path.");
295
296 cchCompare = cch;
297 *pInstallState = vpfnMsiGetComponentPathW(wzProductCode, wzComponentId, *psczValue, &cch);
298 if (INSTALLSTATE_MOREDATA == *pInstallState)
299 {
300 ++cch;
301 hr = StrAlloc(psczValue, cch);
302 WiuExitOnFailure(hr, "Failed to reallocate string for component path.");
303
304 cchCompare = cch;
305 *pInstallState = vpfnMsiGetComponentPathW(wzProductCode, wzComponentId, *psczValue, &cch);
306 }
307
308 if (INSTALLSTATE_INVALIDARG == *pInstallState)
309 {
310 hr = E_INVALIDARG;
311 WiuExitOnRootFailure(hr, "Invalid argument when getting component path.");
312 }
313 else if (INSTALLSTATE_UNKNOWN == *pInstallState)
314 {
315 ExitFunction();
316 }
317
318 // If the actual path length is greater than or equal to the original buffer
319 // allocate a larger buffer and get the path again, just in case we are
320 // missing any part of the path.
321 if (cchCompare <= cch)
322 {
323 ++cch;
324 hr = StrAlloc(psczValue, cch);
325 WiuExitOnFailure(hr, "Failed to reallocate string for component path.");
326
327 *pInstallState = vpfnMsiGetComponentPathW(wzProductCode, wzComponentId, *psczValue, &cch);
328 }
329
330LExit:
331 return hr;
332}
333
334
335extern "C" HRESULT DAPI WiuLocateComponent(
336 __in_z LPCWSTR wzComponentId,
337 __out INSTALLSTATE* pInstallState,
338 __out_z LPWSTR* psczValue
339 )
340{
341 HRESULT hr = S_OK;
342 DWORD cch = WIU_GOOD_ENOUGH_PROPERTY_LENGTH;
343 DWORD cchCompare;
344
345 hr = StrAlloc(psczValue, cch);
346 WiuExitOnFailure(hr, "Failed to allocate string for component path.");
347
348 cchCompare = cch;
349 *pInstallState = vpfnMsiLocateComponentW(wzComponentId, *psczValue, &cch);
350 if (INSTALLSTATE_MOREDATA == *pInstallState)
351 {
352 ++cch;
353 hr = StrAlloc(psczValue, cch);
354 WiuExitOnFailure(hr, "Failed to reallocate string for component path.");
355
356 cchCompare = cch;
357 *pInstallState = vpfnMsiLocateComponentW(wzComponentId, *psczValue, &cch);
358 }
359
360 if (INSTALLSTATE_INVALIDARG == *pInstallState)
361 {
362 hr = E_INVALIDARG;
363 WiuExitOnRootFailure(hr, "Invalid argument when locating component.");
364 }
365 else if (INSTALLSTATE_UNKNOWN == *pInstallState)
366 {
367 ExitFunction();
368 }
369
370 // If the actual path length is greater than or equal to the original buffer
371 // allocate a larger buffer and get the path again, just in case we are
372 // missing any part of the path.
373 if (cchCompare <= cch)
374 {
375 ++cch;
376 hr = StrAlloc(psczValue, cch);
377 WiuExitOnFailure(hr, "Failed to reallocate string for component path.");
378
379 *pInstallState = vpfnMsiLocateComponentW(wzComponentId, *psczValue, &cch);
380 }
381
382LExit:
383 return hr;
384}
385
386
387extern "C" HRESULT DAPI WiuQueryFeatureState(
388 __in_z LPCWSTR wzProduct,
389 __in_z LPCWSTR wzFeature,
390 __out INSTALLSTATE* pInstallState
391 )
392{
393 HRESULT hr = S_OK;
394
395 *pInstallState = vpfnMsiQueryFeatureStateW(wzProduct, wzFeature);
396 if (INSTALLSTATE_INVALIDARG == *pInstallState)
397 {
398 hr = E_INVALIDARG;
399 WiuExitOnRootFailure(hr, "Failed to query state of feature: %ls in product: %ls", wzFeature, wzProduct);
400 }
401
402LExit:
403 return hr;
404}
405
406
407extern "C" HRESULT DAPI WiuGetProductInfo(
408 __in_z LPCWSTR wzProductCode,
409 __in_z LPCWSTR wzProperty,
410 __out LPWSTR* psczValue
411 )
412{
413 HRESULT hr = S_OK;
414 UINT er = ERROR_SUCCESS;
415 DWORD cch = WIU_GOOD_ENOUGH_PROPERTY_LENGTH;
416
417 hr = StrAlloc(psczValue, cch);
418 WiuExitOnFailure(hr, "Failed to allocate string for product info.");
419
420 er = vpfnMsiGetProductInfoW(wzProductCode, wzProperty, *psczValue, &cch);
421 if (ERROR_MORE_DATA == er)
422 {
423 ++cch;
424 hr = StrAlloc(psczValue, cch);
425 WiuExitOnFailure(hr, "Failed to reallocate string for product info.");
426
427 er = vpfnMsiGetProductInfoW(wzProductCode, wzProperty, *psczValue, &cch);
428 }
429 WiuExitOnWin32Error(er, hr, "Failed to get product info.");
430
431LExit:
432 return hr;
433}
434
435
436extern "C" HRESULT DAPI WiuGetProductInfoEx(
437 __in_z LPCWSTR wzProductCode,
438 __in_z_opt LPCWSTR wzUserSid,
439 __in MSIINSTALLCONTEXT dwContext,
440 __in_z LPCWSTR wzProperty,
441 __out LPWSTR* psczValue
442 )
443{
444 HRESULT hr = S_OK;
445 UINT er = ERROR_SUCCESS;
446 DWORD cch = WIU_GOOD_ENOUGH_PROPERTY_LENGTH;
447
448 if (!vpfnMsiGetProductInfoExW)
449 {
450 hr = WiuGetProductInfo(wzProductCode, wzProperty, psczValue);
451 WiuExitOnFailure(hr, "Failed to get product info when extended info was not available.");
452
453 ExitFunction();
454 }
455
456 hr = StrAlloc(psczValue, cch);
457 WiuExitOnFailure(hr, "Failed to allocate string for extended product info.");
458
459 er = vpfnMsiGetProductInfoExW(wzProductCode, wzUserSid, dwContext, wzProperty, *psczValue, &cch);
460 if (ERROR_MORE_DATA == er)
461 {
462 ++cch;
463 hr = StrAlloc(psczValue, cch);
464 WiuExitOnFailure(hr, "Failed to reallocate string for extended product info.");
465
466 er = vpfnMsiGetProductInfoExW(wzProductCode, wzUserSid, dwContext, wzProperty, *psczValue, &cch);
467 }
468 WiuExitOnWin32Error(er, hr, "Failed to get extended product info.");
469
470LExit:
471 return hr;
472}
473
474
475extern "C" HRESULT DAPI WiuGetProductProperty(
476 __in MSIHANDLE hProduct,
477 __in_z LPCWSTR wzProperty,
478 __out LPWSTR* psczValue
479 )
480{
481 HRESULT hr = S_OK;
482 UINT er = ERROR_SUCCESS;
483 DWORD cch = WIU_GOOD_ENOUGH_PROPERTY_LENGTH;
484
485 hr = StrAlloc(psczValue, cch);
486 WiuExitOnFailure(hr, "Failed to allocate string for product property.");
487
488 er = ::MsiGetProductPropertyW(hProduct, wzProperty, *psczValue, &cch);
489 if (ERROR_MORE_DATA == er)
490 {
491 ++cch;
492 hr = StrAlloc(psczValue, cch);
493 WiuExitOnFailure(hr, "Failed to reallocate string for product property.");
494
495 er = ::MsiGetProductPropertyW(hProduct, wzProperty, *psczValue, &cch);
496 }
497 WiuExitOnWin32Error(er, hr, "Failed to get product property.");
498
499LExit:
500 return hr;
501}
502
503
504extern "C" HRESULT DAPI WiuGetPatchInfoEx(
505 __in_z LPCWSTR wzPatchCode,
506 __in_z LPCWSTR wzProductCode,
507 __in_z_opt LPCWSTR wzUserSid,
508 __in MSIINSTALLCONTEXT dwContext,
509 __in_z LPCWSTR wzProperty,
510 __out LPWSTR* psczValue
511 )
512{
513 HRESULT hr = S_OK;
514 UINT er = ERROR_SUCCESS;
515 DWORD cch = WIU_GOOD_ENOUGH_PROPERTY_LENGTH;
516
517 if (!vpfnMsiGetPatchInfoExW)
518 {
519 ExitFunction1(hr = E_NOTIMPL);
520 }
521
522 hr = StrAlloc(psczValue, cch);
523 WiuExitOnFailure(hr, "Failed to allocate string for extended patch info.");
524
525 er = vpfnMsiGetPatchInfoExW(wzPatchCode, wzProductCode, wzUserSid, dwContext, wzProperty, *psczValue, &cch);
526 if (ERROR_MORE_DATA == er)
527 {
528 ++cch;
529 hr = StrAlloc(psczValue, cch);
530 WiuExitOnFailure(hr, "Failed to reallocate string for extended patch info.");
531
532 er = vpfnMsiGetPatchInfoExW(wzPatchCode, wzProductCode, wzUserSid, dwContext, wzProperty, *psczValue, &cch);
533 }
534 WiuExitOnWin32Error(er, hr, "Failed to get extended patch info.");
535
536LExit:
537 return hr;
538}
539
540
541extern "C" HRESULT DAPI WiuDeterminePatchSequence(
542 __in_z LPCWSTR wzProductCode,
543 __in_z_opt LPCWSTR wzUserSid,
544 __in MSIINSTALLCONTEXT context,
545 __in PMSIPATCHSEQUENCEINFOW pPatchInfo,
546 __in DWORD cPatchInfo
547 )
548{
549 HRESULT hr = S_OK;
550 DWORD er = ERROR_SUCCESS;
551
552 if (!vpfnMsiDeterminePatchSequenceW)
553 {
554 ExitFunction1(hr = E_NOTIMPL);
555 }
556
557 er = vpfnMsiDeterminePatchSequenceW(wzProductCode, wzUserSid, context, cPatchInfo, pPatchInfo);
558 WiuExitOnWin32Error(er, hr, "Failed to determine patch sequence for product code.");
559
560LExit:
561 return hr;
562}
563
564
565extern "C" HRESULT DAPI WiuDetermineApplicablePatches(
566 __in_z LPCWSTR wzProductPackagePath,
567 __in PMSIPATCHSEQUENCEINFOW pPatchInfo,
568 __in DWORD cPatchInfo
569 )
570{
571 HRESULT hr = S_OK;
572 DWORD er = ERROR_SUCCESS;
573
574 if (!vpfnMsiDetermineApplicablePatchesW)
575 {
576 ExitFunction1(hr = E_NOTIMPL);
577 }
578
579 er = vpfnMsiDetermineApplicablePatchesW(wzProductPackagePath, cPatchInfo, pPatchInfo);
580 WiuExitOnWin32Error(er, hr, "Failed to determine applicable patches for product package.");
581
582LExit:
583 return hr;
584}
585
586
587extern "C" HRESULT DAPI WiuEnumProducts(
588 __in DWORD iProductIndex,
589 __out_ecount(MAX_GUID_CHARS + 1) LPWSTR wzProductCode
590 )
591{
592 HRESULT hr = S_OK;
593 DWORD er = ERROR_SUCCESS;
594
595 er = vpfnMsiEnumProductsW(iProductIndex, wzProductCode);
596 if (ERROR_NO_MORE_ITEMS == er)
597 {
598 ExitFunction1(hr = HRESULT_FROM_WIN32(er));
599 }
600 WiuExitOnWin32Error(er, hr, "Failed to enumerate products.");
601
602LExit:
603 return hr;
604}
605
606
607extern "C" HRESULT DAPI WiuEnumProductsEx(
608 __in_z_opt LPCWSTR wzProductCode,
609 __in_z_opt LPCWSTR wzUserSid,
610 __in DWORD dwContext,
611 __in DWORD dwIndex,
612 __out_opt WCHAR wzInstalledProductCode[39],
613 __out_opt MSIINSTALLCONTEXT *pdwInstalledContext,
614 __out_opt LPWSTR wzSid,
615 __inout_opt LPDWORD pcchSid
616 )
617{
618 HRESULT hr = S_OK;
619 DWORD er = ERROR_SUCCESS;
620
621 if (!vpfnMsiEnumProductsExW)
622 {
623 ExitFunction1(hr = E_NOTIMPL);
624 }
625
626 er = vpfnMsiEnumProductsExW(wzProductCode, wzUserSid, dwContext, dwIndex, wzInstalledProductCode, pdwInstalledContext, wzSid, pcchSid);
627 if (ERROR_NO_MORE_ITEMS == er)
628 {
629 ExitFunction1(hr = HRESULT_FROM_WIN32(er));
630 }
631 WiuExitOnWin32Error(er, hr, "Failed to enumerate products.");
632
633LExit:
634 return hr;
635}
636
637
638extern "C" HRESULT DAPI WiuEnumRelatedProducts(
639 __in_z LPCWSTR wzUpgradeCode,
640 __in DWORD iProductIndex,
641 __out_ecount(MAX_GUID_CHARS + 1) LPWSTR wzProductCode
642 )
643{
644 HRESULT hr = S_OK;
645 DWORD er = ERROR_SUCCESS;
646
647 er = vpfnMsiEnumRelatedProductsW(wzUpgradeCode, 0, iProductIndex, wzProductCode);
648 if (ERROR_NO_MORE_ITEMS == er)
649 {
650 ExitFunction1(hr = HRESULT_FROM_WIN32(er));
651 }
652 WiuExitOnWin32Error(er, hr, "Failed to enumerate related products for updgrade code: %ls", wzUpgradeCode);
653
654LExit:
655 return hr;
656}
657
658/********************************************************************
659 WiuEnumRelatedProductCodes - Returns an array of related products for a given upgrade code.
660
661 Parameters:
662 wzUpgradeCode - The upgrade code that will be used to find the related products.
663 prgsczProductCodes - Pointer to the array that will contain the product codes.
664 pcRelatedProducts - Returns the count of the number of related products found.
665 fReturnHighestVersionOnly - When set to "TRUE", will only return the product code of the highest version found.
666********************************************************************/
667extern "C" HRESULT DAPI WiuEnumRelatedProductCodes(
668 __in_z LPCWSTR wzUpgradeCode,
669 __deref_out_ecount_opt(*pcRelatedProducts) LPWSTR** prgsczProductCodes,
670 __out DWORD* pcRelatedProducts,
671 __in BOOL fReturnHighestVersionOnly
672 )
673{
674 HRESULT hr = S_OK;
675 WCHAR wzCurrentProductCode[MAX_GUID_CHARS + 1] = { };
676 LPWSTR sczInstalledVersion = NULL;
677 VERUTIL_VERSION* pCurrentVersion = NULL;
678 VERUTIL_VERSION* pHighestVersion = NULL;
679 int nCompare = 0;
680
681 // make sure we start at zero
682 *pcRelatedProducts = 0;
683
684 for (DWORD i = 0; ; ++i)
685 {
686 hr = WiuEnumRelatedProducts(wzUpgradeCode, i, wzCurrentProductCode);
687
688 if (E_NOMOREITEMS == hr)
689 {
690 hr = S_OK;
691 break;
692 }
693 WiuExitOnFailure(hr, "Failed to enumerate related products for upgrade code: %ls", wzUpgradeCode);
694
695 if (fReturnHighestVersionOnly)
696 {
697 // try to get the version but if the product registration is broken
698 // (for whatever reason), skip this product
699 hr = WiuGetProductInfo(wzCurrentProductCode, L"VersionString", &sczInstalledVersion);
700 if (FAILED(hr))
701 {
702 WiuExitTrace(hr, "Could not get product version for product code: %ls, skipping...", wzCurrentProductCode);
703 continue;
704 }
705
706 hr = VerParseVersion(sczInstalledVersion, 0, FALSE, &pCurrentVersion);
707 WiuExitOnFailure(hr, "Failed to parse version: %ls for product code: %ls", sczInstalledVersion, wzCurrentProductCode);
708
709 if (pCurrentVersion->fInvalid)
710 {
711 WiuExitTrace(E_INVALIDDATA, "Enumerated msi package with invalid version, product code: '%1!ls!', version: '%2!ls!'");
712 }
713
714 // if this is the first product found then it is the highest version (for now)
715 if (!pHighestVersion)
716 {
717 pHighestVersion = pCurrentVersion;
718 pCurrentVersion = NULL;
719 }
720 else
721 {
722 hr = VerCompareParsedVersions(pCurrentVersion, pHighestVersion, &nCompare);
723 WiuExitOnFailure(hr, "Failed to compare version '%ls' to highest version: '%ls'", pCurrentVersion->sczVersion, pHighestVersion->sczVersion);
724
725 // if this is the highest version encountered so far then overwrite
726 // the first item in the array (there will never be more than one item)
727 if (nCompare > 0)
728 {
729 ReleaseVerutilVersion(pHighestVersion);
730 pHighestVersion = pCurrentVersion;
731 pCurrentVersion = NULL;
732
733 hr = StrAllocString(prgsczProductCodes[0], wzCurrentProductCode, 0);
734 WiuExitOnFailure(hr, "Failed to update array with higher versioned product code.");
735 }
736 else
737 {
738 ReleaseVerutilVersion(pCurrentVersion);
739 }
740
741 // continue here as we don't want anything else added to the list
742 continue;
743 }
744 }
745
746 hr = StrArrayAllocString(prgsczProductCodes, (LPUINT)(pcRelatedProducts), wzCurrentProductCode, 0);
747 WiuExitOnFailure(hr, "Failed to add product code to array.");
748 }
749
750LExit:
751 ReleaseVerutilVersion(pCurrentVersion);
752 ReleaseVerutilVersion(pHighestVersion);
753 ReleaseStr(sczInstalledVersion);
754 return hr;
755}
756
757
758extern "C" HRESULT DAPI WiuEnableLog(
759 __in DWORD dwLogMode,
760 __in_z LPCWSTR wzLogFile,
761 __in DWORD dwLogAttributes
762 )
763{
764 HRESULT hr = S_OK;
765 DWORD er = ERROR_SUCCESS;
766
767 er = vpfnMsiEnableLogW(dwLogMode, wzLogFile, dwLogAttributes);
768 WiuExitOnWin32Error(er, hr, "Failed to enable MSI internal logging.");
769
770LExit:
771 return hr;
772}
773
774
775extern "C" HRESULT DAPI WiuInitializeInternalUI(
776 __in INSTALLUILEVEL internalUILevel,
777 __in_opt HWND hwndParent,
778 __in WIU_MSI_EXECUTE_CONTEXT* pExecuteContext
779 )
780{
781 HRESULT hr = S_OK;
782
783 memset(pExecuteContext, 0, sizeof(WIU_MSI_EXECUTE_CONTEXT));
784
785 pExecuteContext->previousInstallUILevel = vpfnMsiSetInternalUI(internalUILevel, &hwndParent);
786 pExecuteContext->hwndPreviousParentWindow = hwndParent;
787
788 if (INSTALLUILEVEL_NOCHANGE == pExecuteContext->previousInstallUILevel)
789 {
790 hr = E_INVALIDARG;
791 }
792
793 return hr;
794}
795
796
797extern "C" HRESULT DAPI WiuInitializeExternalUI(
798 __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler,
799 __in INSTALLUILEVEL internalUILevel,
800 __in_opt HWND hwndParent,
801 __in LPVOID pvContext,
802 __in BOOL fRollback,
803 __in WIU_MSI_EXECUTE_CONTEXT* pExecuteContext
804 )
805{
806 HRESULT hr = S_OK;
807 DWORD er = ERROR_SUCCESS;
808
809 DWORD dwMessageFilter = INSTALLLOGMODE_INITIALIZE | INSTALLLOGMODE_TERMINATE |
810 INSTALLLOGMODE_FATALEXIT | INSTALLLOGMODE_ERROR | INSTALLLOGMODE_WARNING |
811 INSTALLLOGMODE_RESOLVESOURCE | INSTALLLOGMODE_OUTOFDISKSPACE |
812 INSTALLLOGMODE_ACTIONSTART | INSTALLLOGMODE_ACTIONDATA | INSTALLLOGMODE_COMMONDATA |
813 INSTALLLOGMODE_PROGRESS | INSTALLLOGMODE_FILESINUSE;
814
815 if (MAKEDWORD(0, 4) <= vdwMsiDllMajorMinor)
816 {
817 dwMessageFilter |= INSTALLLOGMODE_RMFILESINUSE;
818 }
819
820 // Wire the internal and external UI handler.
821 hr = WiuInitializeInternalUI(internalUILevel, hwndParent, pExecuteContext);
822 WiuExitOnFailure(hr, "Failed to set internal UI level and window.");
823
824 pExecuteContext->fRollback = fRollback;
825 pExecuteContext->pfnMessageHandler = pfnMessageHandler;
826 pExecuteContext->pvContext = pvContext;
827
828 // If the external UI record is available (MSI version >= 3.1) use it but fall back to the standard external
829 // UI handler if necesary.
830 if (vpfnMsiSetExternalUIRecord)
831 {
832 er = vpfnMsiSetExternalUIRecord(InstallEngineRecordCallback, dwMessageFilter, pExecuteContext, &pExecuteContext->pfnPreviousExternalUIRecord);
833 WiuExitOnWin32Error(er, hr, "Failed to wire up external UI record handler.");
834 pExecuteContext->fSetPreviousExternalUIRecord = TRUE;
835 }
836 else
837 {
838 pExecuteContext->pfnPreviousExternalUI = vpfnMsiSetExternalUIW(InstallEngineCallback, dwMessageFilter, pExecuteContext);
839 pExecuteContext->fSetPreviousExternalUI = TRUE;
840 }
841
842LExit:
843 return hr;
844}
845
846
847extern "C" void DAPI WiuUninitializeExternalUI(
848 __in WIU_MSI_EXECUTE_CONTEXT* pExecuteContext
849 )
850{
851 if (INSTALLUILEVEL_NOCHANGE != pExecuteContext->previousInstallUILevel)
852 {
853 pExecuteContext->previousInstallUILevel = vpfnMsiSetInternalUI(pExecuteContext->previousInstallUILevel, &pExecuteContext->hwndPreviousParentWindow);
854 }
855
856 if (pExecuteContext->fSetPreviousExternalUI) // unset the UI handler
857 {
858 vpfnMsiSetExternalUIW(pExecuteContext->pfnPreviousExternalUI, 0, NULL);
859 }
860
861 if (pExecuteContext->fSetPreviousExternalUIRecord) // unset the UI record handler
862 {
863 vpfnMsiSetExternalUIRecord(pExecuteContext->pfnPreviousExternalUIRecord, 0, NULL, NULL);
864 }
865
866 memset(pExecuteContext, 0, sizeof(WIU_MSI_EXECUTE_CONTEXT));
867}
868
869
870extern "C" HRESULT DAPI WiuConfigureProductEx(
871 __in_z LPCWSTR wzProduct,
872 __in int iInstallLevel,
873 __in INSTALLSTATE eInstallState,
874 __in_z LPCWSTR wzCommandLine,
875 __out WIU_RESTART* pRestart
876 )
877{
878 HRESULT hr = S_OK;
879 DWORD er = ERROR_SUCCESS;
880
881 er = vpfnMsiConfigureProductExW(wzProduct, iInstallLevel, eInstallState, wzCommandLine);
882 er = CheckForRestartErrorCode(er, pRestart);
883 WiuExitOnWin32Error(er, hr, "Failed to configure product: %ls", wzProduct);
884
885LExit:
886 return hr;
887}
888
889
890extern "C" HRESULT DAPI WiuInstallProduct(
891 __in_z LPCWSTR wzPackagePath,
892 __in_z LPCWSTR wzCommandLine,
893 __out WIU_RESTART* pRestart
894 )
895{
896 HRESULT hr = S_OK;
897 DWORD er = ERROR_SUCCESS;
898
899 er = vpfnMsiInstallProductW(wzPackagePath, wzCommandLine);
900 er = CheckForRestartErrorCode(er, pRestart);
901 WiuExitOnWin32Error(er, hr, "Failed to install product: %ls", wzPackagePath);
902
903LExit:
904 return hr;
905}
906
907
908extern "C" HRESULT DAPI WiuRemovePatches(
909 __in_z LPCWSTR wzPatchList,
910 __in_z LPCWSTR wzProductCode,
911 __in_z LPCWSTR wzPropertyList,
912 __out WIU_RESTART* pRestart
913 )
914{
915 HRESULT hr = S_OK;
916 DWORD er = ERROR_SUCCESS;
917
918 er = vpfnMsiRemovePatchesW(wzPatchList, wzProductCode, INSTALLTYPE_SINGLE_INSTANCE, wzPropertyList);
919 er = CheckForRestartErrorCode(er, pRestart);
920 WiuExitOnWin32Error(er, hr, "Failed to remove patches.");
921
922LExit:
923 return hr;
924}
925
926
927extern "C" HRESULT DAPI WiuSourceListAddSourceEx(
928 __in_z LPCWSTR wzProductCodeOrPatchCode,
929 __in_z_opt LPCWSTR wzUserSid,
930 __in MSIINSTALLCONTEXT dwContext,
931 __in DWORD dwCode,
932 __in_z LPCWSTR wzSource,
933 __in_opt DWORD dwIndex
934 )
935{
936 HRESULT hr = S_OK;
937 DWORD er = ERROR_SUCCESS;
938
939 er = vpfnMsiSourceListAddSourceExW(wzProductCodeOrPatchCode, wzUserSid, dwContext, MSISOURCETYPE_NETWORK | dwCode, wzSource, dwIndex);
940 WiuExitOnWin32Error(er, hr, "Failed to add source.");
941
942LExit:
943 return hr;
944}
945
946extern "C" BOOL DAPI WiuIsMsiTransactionSupported(
947 )
948{
949 return vpfnMsiBeginTransaction && vpfnMsiEndTransaction;
950}
951
952extern "C" HRESULT DAPI WiuBeginTransaction(
953 __in_z LPCWSTR szName,
954 __in DWORD dwTransactionAttributes,
955 __out MSIHANDLE * phTransactionHandle,
956 __out HANDLE * phChangeOfOwnerEvent,
957 __in DWORD dwLogMode,
958 __in_z LPCWSTR szLogPath
959 )
960{
961 HRESULT hr = S_OK;
962 DWORD er = ERROR_SUCCESS;
963
964 if (!WiuIsMsiTransactionSupported())
965 {
966 WiuExitOnFailure(hr = E_NOTIMPL, "Msi transactions are not supported");
967 }
968
969 hr = WiuEnableLog(dwLogMode, szLogPath, INSTALLLOGATTRIBUTES_APPEND);
970 WiuExitOnFailure(hr, "Failed to enable logging for MSI transaction");
971
972 er = vpfnMsiBeginTransaction(szName, dwTransactionAttributes, phTransactionHandle, phChangeOfOwnerEvent);
973 WiuExitOnWin32Error(er, hr, "Failed to begin transaction.");
974
975LExit:
976 return hr;
977}
978
979extern "C" HRESULT DAPI WiuEndTransaction(
980 __in DWORD dwTransactionState,
981 __in DWORD dwLogMode,
982 __in_z LPCWSTR szLogPath
983 )
984{
985 HRESULT hr = S_OK;
986 DWORD er = ERROR_SUCCESS;
987
988 if (!WiuIsMsiTransactionSupported())
989 {
990 WiuExitOnFailure(hr = E_NOTIMPL, "Msi transactions are not supported");
991 }
992
993 hr = WiuEnableLog(dwLogMode, szLogPath, INSTALLLOGATTRIBUTES_APPEND);
994 WiuExitOnFailure(hr, "Failed to enable logging for MSI transaction");
995
996 er = vpfnMsiEndTransaction(dwTransactionState);
997 WiuExitOnWin32Error(er, hr, "Failed to end transaction.");
998
999LExit:
1000 return hr;
1001}
1002
1003
1004
1005static DWORD CheckForRestartErrorCode(
1006 __in DWORD dwErrorCode,
1007 __out WIU_RESTART* pRestart
1008 )
1009{
1010 switch (dwErrorCode)
1011 {
1012 case ERROR_SUCCESS_REBOOT_REQUIRED:
1013 case ERROR_SUCCESS_RESTART_REQUIRED:
1014 *pRestart = WIU_RESTART_REQUIRED;
1015 dwErrorCode = ERROR_SUCCESS;
1016 break;
1017
1018 case ERROR_SUCCESS_REBOOT_INITIATED:
1019 case ERROR_INSTALL_SUSPEND:
1020 *pRestart = WIU_RESTART_INITIATED;
1021 dwErrorCode = ERROR_SUCCESS;
1022 break;
1023 }
1024
1025 return dwErrorCode;
1026}
1027
1028static INT CALLBACK InstallEngineCallback(
1029 __in LPVOID pvContext,
1030 __in UINT uiMessage,
1031 __in_z_opt LPCWSTR wzMessage
1032 )
1033{
1034 INT nResult = IDNOACTION;
1035 WIU_MSI_EXECUTE_CONTEXT* pContext = (WIU_MSI_EXECUTE_CONTEXT*)pvContext;
1036 INSTALLMESSAGE mt = static_cast<INSTALLMESSAGE>(0xFF000000 & uiMessage);
1037 UINT uiFlags = 0x00FFFFFF & uiMessage;
1038
1039 if (wzMessage)
1040 {
1041 if (INSTALLMESSAGE_PROGRESS == mt)
1042 {
1043 nResult = HandleInstallProgress(pContext, wzMessage, NULL);
1044 }
1045 else
1046 {
1047 nResult = HandleInstallMessage(pContext, mt, uiFlags, wzMessage, NULL);
1048 }
1049 }
1050
1051 return nResult;
1052}
1053
1054static INT CALLBACK InstallEngineRecordCallback(
1055 __in LPVOID pvContext,
1056 __in UINT uiMessage,
1057 __in_opt MSIHANDLE hRecord
1058 )
1059{
1060 INT nResult = IDNOACTION;
1061 HRESULT hr = S_OK;
1062 WIU_MSI_EXECUTE_CONTEXT* pContext = (WIU_MSI_EXECUTE_CONTEXT*)pvContext;
1063
1064 INSTALLMESSAGE mt = static_cast<INSTALLMESSAGE>(0xFF000000 & uiMessage);
1065 UINT uiFlags = 0x00FFFFFF & uiMessage;
1066 LPWSTR sczMessage = NULL;
1067 DWORD cchMessage = 0;
1068
1069 if (hRecord)
1070 {
1071 if (INSTALLMESSAGE_PROGRESS == mt)
1072 {
1073 nResult = HandleInstallProgress(pContext, NULL, hRecord);
1074 }
1075 else
1076 {
1077 // create formated message string
1078#pragma prefast(push)
1079#pragma prefast(disable:6298) // docs explicitly say this is a valid option for getting the buffer size
1080 DWORD er = ::MsiFormatRecordW(NULL, hRecord, L"", &cchMessage);
1081#pragma prefast(pop)
1082 if (ERROR_MORE_DATA == er || ERROR_SUCCESS == er)
1083 {
1084 hr = StrAlloc(&sczMessage, ++cchMessage);
1085 }
1086 else
1087 {
1088 hr = HRESULT_FROM_WIN32(er);
1089 }
1090 WiuExitOnFailure(hr, "Failed to allocate string for formated message.");
1091
1092 er = ::MsiFormatRecordW(NULL, hRecord, sczMessage, &cchMessage);
1093 WiuExitOnWin32Error(er, hr, "Failed to format message record.");
1094
1095 // Pass to handler including both the formated message and the original record.
1096 nResult = HandleInstallMessage(pContext, mt, uiFlags, sczMessage, hRecord);
1097 }
1098 }
1099
1100LExit:
1101 ReleaseStr(sczMessage);
1102 return nResult;
1103}
1104
1105static INT HandleInstallMessage(
1106 __in WIU_MSI_EXECUTE_CONTEXT* pContext,
1107 __in INSTALLMESSAGE mt,
1108 __in UINT uiFlags,
1109 __in_z LPCWSTR wzMessage,
1110 __in_opt MSIHANDLE hRecord
1111 )
1112{
1113 INT nResult = IDNOACTION;
1114
1115Trace(REPORT_STANDARD, "MSI install[%x]: %ls", pContext->dwCurrentProgressIndex, wzMessage);
1116
1117 // Handle the message.
1118 switch (mt)
1119 {
1120 case INSTALLMESSAGE_INITIALIZE: // this message is received prior to internal UI initialization, no string data
1121 ResetProgress(pContext);
1122 break;
1123
1124 case INSTALLMESSAGE_TERMINATE: // sent after UI termination, no string data
1125 break;
1126
1127 case INSTALLMESSAGE_ACTIONSTART:
1128 if (WIU_MSI_PROGRESS_INVALID != pContext->dwCurrentProgressIndex && pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fEnableActionData)
1129 {
1130 pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fEnableActionData = FALSE;
1131 }
1132
1133 nResult = SendMsiMessage(pContext, mt, uiFlags, wzMessage, hRecord);
1134 break;
1135
1136 case INSTALLMESSAGE_ACTIONDATA:
1137 if (WIU_MSI_PROGRESS_INVALID != pContext->dwCurrentProgressIndex && pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fEnableActionData)
1138 {
1139 if (pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fMoveForward)
1140 {
1141 pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwCompleted += pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwStep;
1142 }
1143 else // rollback.
1144 {
1145 pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwCompleted -= pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwStep;
1146 }
1147
1148 nResult = SendProgressUpdate(pContext);
1149 }
1150 else
1151 {
1152 nResult = SendMsiMessage(pContext, mt, uiFlags, wzMessage, hRecord);
1153 }
1154 break;
1155
1156 case INSTALLMESSAGE_OUTOFDISKSPACE: __fallthrough;
1157 case INSTALLMESSAGE_FATALEXIT: __fallthrough;
1158 case INSTALLMESSAGE_ERROR:
1159 nResult = SendErrorMessage(pContext, uiFlags, wzMessage, hRecord);
1160 break;
1161
1162 case INSTALLMESSAGE_FILESINUSE:
1163 case INSTALLMESSAGE_RMFILESINUSE:
1164 nResult = SendFilesInUseMessage(pContext, hRecord, INSTALLMESSAGE_RMFILESINUSE == mt);
1165 break;
1166
1167/*
1168#if 0
1169 case INSTALLMESSAGE_COMMONDATA:
1170 if (L'1' == wzMessage[0] && L':' == wzMessage[1] && L' ' == wzMessage[2])
1171 {
1172 if (L'0' == wzMessage[3])
1173 {
1174 // TODO: handle the language common data message.
1175 lres = IDOK;
1176 return lres;
1177 }
1178 else if (L'1' == wzMessage[3])
1179 {
1180 // TODO: really handle sending the caption.
1181 lres = ::SendSuxMessage(pInstallContext->pSetupUXInformation, SRM_EXEC_SET_CAPTION, uiFlags, reinterpret_cast<LPARAM>(wzMessage + 3));
1182 return lres;
1183 }
1184 else if (L'2' == wzMessage[3])
1185 {
1186 // TODO: really handle sending the cancel button status.
1187 lres = ::SendSuxMessage(pInstallContext->pSetupUXInformation, SRM_EXEC_SET_CANCEL, uiFlags, reinterpret_cast<LPARAM>(wzMessage + 3));
1188 return lres;
1189 }
1190 }
1191 break;
1192#endif
1193*/
1194
1195 //case INSTALLMESSAGE_WARNING:
1196 //case INSTALLMESSAGE_USER:
1197 //case INSTALLMESSAGE_INFO:
1198 //case INSTALLMESSAGE_SHOWDIALOG: // sent prior to display of authored dialog or wizard
1199 default:
1200 nResult = SendMsiMessage(pContext, mt, uiFlags, wzMessage, hRecord);
1201 break;
1202 }
1203
1204 // Always return "no action" (0) for resolve source messages.
1205 return (INSTALLMESSAGE_RESOLVESOURCE == mt) ? IDNOACTION : nResult;
1206}
1207
1208static INT HandleInstallProgress(
1209 __in WIU_MSI_EXECUTE_CONTEXT* pContext,
1210 __in_z_opt LPCWSTR wzMessage,
1211 __in_opt MSIHANDLE hRecord
1212 )
1213{
1214 HRESULT hr = S_OK;
1215 INT nResult = IDNOACTION;
1216 INT iFields[4] = { };
1217 INT cFields = 0;
1218 LPCWSTR pwz = NULL;
1219 DWORD cch = 0;
1220
1221 // get field values
1222 if (hRecord)
1223 {
1224 cFields = ::MsiRecordGetFieldCount(hRecord);
1225 cFields = min(cFields, countof(iFields)); // avoid buffer overrun if there are more fields than our buffer can hold
1226 for (INT i = 0; i < cFields; ++i)
1227 {
1228 iFields[i] = ::MsiRecordGetInteger(hRecord, i + 1);
1229 }
1230 }
1231 else
1232 {
1233 Assert(wzMessage);
1234
1235 // parse message string
1236 pwz = wzMessage;
1237 while (cFields < 4)
1238 {
1239 // check if we have the start of a valid part
1240 if ((L'1' + cFields) != pwz[0] || L':' != pwz[1] || L' ' != pwz[2])
1241 {
1242 break;
1243 }
1244 pwz += 3;
1245
1246 // find character count of number
1247 cch = 0;
1248 while (pwz[cch] && L' ' != pwz[cch])
1249 {
1250 ++cch;
1251 }
1252
1253 // parse number
1254 hr = StrStringToInt32(pwz, cch, &iFields[cFields]);
1255 WiuExitOnFailure(hr, "Failed to parse MSI message part.");
1256
1257 // increment field count
1258 ++cFields;
1259 }
1260 }
1261
1262#ifdef _DEBUG
1263 WCHAR wz[256];
1264 ::StringCchPrintfW(wz, countof(wz), L"1: %d 2: %d 3: %d 4: %d", iFields[0], iFields[1], iFields[2], iFields[3]);
1265 Trace(REPORT_STANDARD, "MSI progress[%x]: %ls", pContext->dwCurrentProgressIndex, wz);
1266#endif
1267
1268 // Verify that we have the enough field values.
1269 if (1 > cFields)
1270 {
1271 ExitFunction(); // unknown message, bail
1272 }
1273
1274 // Handle based on message type.
1275 switch (iFields[0])
1276 {
1277 case 0: // master progress reset
1278 if (4 > cFields)
1279 {
1280 Trace(REPORT_STANDARD, "INSTALLMESSAGE_PROGRESS - Invalid field count %d, '%ls'", cFields, wzMessage);
1281 ExitFunction();
1282 }
1283 //Trace(REPORT_STANDARD, "INSTALLMESSAGE_PROGRESS - MASTER RESET - %d, %d, %d", iFields[1], iFields[2], iFields[3]);
1284
1285 // Update the index into progress array.
1286 if (WIU_MSI_PROGRESS_INVALID == pContext->dwCurrentProgressIndex)
1287 {
1288 pContext->dwCurrentProgressIndex = 0;
1289 }
1290 else if (pContext->dwCurrentProgressIndex + 1 < countof(pContext->rgMsiProgress))
1291 {
1292 ++pContext->dwCurrentProgressIndex;
1293 }
1294 else
1295 {
1296 hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
1297 WiuExitOnRootFailure(hr, "Insufficient space to hold progress information.");
1298 }
1299
1300 // we only care about the first stage after script execution has started
1301 //if (!pEngineInfo->fMsiProgressScriptInProgress && 1 != iFields[3])
1302 //{
1303 // pEngineInfo->fMsiProgressFinished = TRUE;
1304 //}
1305
1306 pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwTotal = iFields[1];
1307 pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwCompleted = 0 == iFields[2] ? 0 : iFields[1]; // if forward start at 0, if backwards start at max
1308 pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fMoveForward = (0 == iFields[2]);
1309 pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fEnableActionData = FALSE;
1310 pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fScriptInProgress = (1 == iFields[3]);
1311
1312 if (0 == pContext->dwCurrentProgressIndex)
1313 {
1314 // HACK!!! this is a hack courtesy of the Windows Installer team. It seems the script planning phase
1315 // is always off by "about 50". So we'll toss an extra 50 ticks on so that the standard progress
1316 // doesn't go over 100%. If there are any custom actions, they may blow the total so we'll call this
1317 // "close" and deal with the rest.
1318 pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwTotal += 50;
1319 }
1320 break;
1321
1322 case 1: // action info.
1323 if (3 > cFields)
1324 {
1325 Trace(REPORT_STANDARD, "INSTALLMESSAGE_PROGRESS - Invalid field count %d, '%ls'", cFields, wzMessage);
1326 ExitFunction();
1327 }
1328 //Trace(REPORT_STANDARD, "INSTALLMESSAGE_PROGRESS - ACTION INFO - %d, %d, %d", iFields[1], iFields[2], iFields[3]);
1329
1330 if (0 == iFields[2])
1331 {
1332 pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fEnableActionData = FALSE;
1333 }
1334 else
1335 {
1336 pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fEnableActionData = TRUE;
1337 pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwStep = iFields[1];
1338 }
1339 break;
1340
1341 case 2: // progress report.
1342 if (2 > cFields)
1343 {
1344 Trace(REPORT_STANDARD, "INSTALLMESSAGE_PROGRESS - Invalid field count %d, '%ls'", cFields, wzMessage);
1345 break;
1346 }
1347
1348 //Trace(REPORT_STANDARD, "INSTALLMESSAGE_PROGRESS - PROGRESS REPORT - %d, %d, %d", iFields[1], iFields[2], iFields[3]);
1349
1350 if (WIU_MSI_PROGRESS_INVALID == pContext->dwCurrentProgressIndex)
1351 {
1352 break;
1353 }
1354 else if (0 == pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwTotal)
1355 {
1356 break;
1357 }
1358
1359 // Update progress.
1360 if (pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fMoveForward)
1361 {
1362 pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwCompleted += iFields[1];
1363 }
1364 else // rollback.
1365 {
1366 pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwCompleted -= iFields[1];
1367 }
1368 break;
1369
1370 case 3: // extend the progress bar.
1371 pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwTotal += iFields[1];
1372 break;
1373
1374 default:
1375 ExitFunction(); // unknown message, bail
1376 }
1377
1378 // If we have a valid progress index, send an update.
1379 if (WIU_MSI_PROGRESS_INVALID != pContext->dwCurrentProgressIndex)
1380 {
1381 nResult = SendProgressUpdate(pContext);
1382 }
1383
1384LExit:
1385 return nResult;
1386}
1387
1388static INT SendMsiMessage(
1389 __in WIU_MSI_EXECUTE_CONTEXT* pContext,
1390 __in INSTALLMESSAGE mt,
1391 __in UINT uiFlags,
1392 __in_z LPCWSTR wzMessage,
1393 __in_opt MSIHANDLE hRecord
1394 )
1395{
1396 INT nResult = IDNOACTION;
1397 WIU_MSI_EXECUTE_MESSAGE message = { };
1398 LPWSTR* rgsczData = NULL;
1399 DWORD cData = 0;
1400
1401 InitializeMessageData(hRecord, &rgsczData, &cData);
1402
1403 message.type = WIU_MSI_EXECUTE_MESSAGE_MSI_MESSAGE;
1404 message.dwAllowedResults = uiFlags;
1405 message.cData = cData;
1406 message.rgwzData = (LPCWSTR*)rgsczData;
1407 message.msiMessage.mt = mt;
1408 message.msiMessage.wzMessage = wzMessage;
1409 nResult = pContext->pfnMessageHandler(&message, pContext->pvContext);
1410
1411 UninitializeMessageData(rgsczData, cData);
1412 return nResult;
1413}
1414
1415static INT SendErrorMessage(
1416 __in WIU_MSI_EXECUTE_CONTEXT* pContext,
1417 __in UINT uiFlags,
1418 __in_z LPCWSTR wzMessage,
1419 __in_opt MSIHANDLE hRecord
1420 )
1421{
1422 INT nResult = IDNOACTION;
1423 WIU_MSI_EXECUTE_MESSAGE message = { };
1424 DWORD dwErrorCode = 0;
1425 LPWSTR* rgsczData = NULL;
1426 DWORD cData = 0;
1427
1428 if (hRecord)
1429 {
1430 dwErrorCode = ::MsiRecordGetInteger(hRecord, 1);
1431
1432 // Set the recommendation if it's a known error code.
1433 switch (dwErrorCode)
1434 {
1435 case 1605: // continue with install even if there isn't enough room for rollback.
1436 nResult = IDIGNORE;
1437 break;
1438
1439 case 1704: // rollback suspended installs so our install can continue.
1440 nResult = IDOK;
1441 break;
1442 }
1443 }
1444
1445 InitializeMessageData(hRecord, &rgsczData, &cData);
1446
1447 message.type = WIU_MSI_EXECUTE_MESSAGE_ERROR;
1448 message.dwAllowedResults = uiFlags;
1449 message.nResultRecommendation = nResult;
1450 message.cData = cData;
1451 message.rgwzData = (LPCWSTR*)rgsczData;
1452 message.error.dwErrorCode = dwErrorCode;
1453 message.error.wzMessage = wzMessage;
1454 nResult = pContext->pfnMessageHandler(&message, pContext->pvContext);
1455
1456 UninitializeMessageData(rgsczData, cData);
1457 return nResult;
1458}
1459
1460static INT SendFilesInUseMessage(
1461 __in WIU_MSI_EXECUTE_CONTEXT* pContext,
1462 __in_opt MSIHANDLE hRecord,
1463 __in BOOL /*fRestartManagerRequest*/
1464 )
1465{
1466 INT nResult = IDNOACTION;
1467 WIU_MSI_EXECUTE_MESSAGE message = { };
1468 LPWSTR* rgsczData = NULL;
1469 DWORD cData = 0;
1470
1471 InitializeMessageData(hRecord, &rgsczData, &cData);
1472
1473 message.type = WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE;
1474 message.dwAllowedResults = WIU_MB_OKIGNORECANCELRETRY;
1475 message.cData = cData;
1476 message.rgwzData = (LPCWSTR*)rgsczData;
1477 message.msiFilesInUse.cFiles = message.cData; // point the files in use information to the message record information.
1478 message.msiFilesInUse.rgwzFiles = message.rgwzData;
1479 nResult = pContext->pfnMessageHandler(&message, pContext->pvContext);
1480
1481 UninitializeMessageData(rgsczData, cData);
1482 return nResult;
1483}
1484
1485static INT SendProgressUpdate(
1486 __in WIU_MSI_EXECUTE_CONTEXT* pContext
1487 )
1488{
1489 int nResult = IDNOACTION;
1490 DWORD dwPercentage = 0; // number representing 0 - 100%
1491 WIU_MSI_EXECUTE_MESSAGE message = { };
1492
1493 //DWORD dwMsiProgressTotal = pEngineInfo->dwMsiProgressTotal;
1494 //DWORD dwMsiProgressComplete = pEngineInfo->dwMsiProgressComplete; //min(dwMsiProgressTotal, pEngineInfo->dwMsiProgressComplete);
1495 //double dProgressGauge = 0;
1496 //double dProgressStageTotal = (double)pEngineInfo->qwProgressStageTotal;
1497
1498 // Calculate progress for the phases of Windows Installer.
1499 // TODO: handle upgrade progress which would add another phase.
1500 dwPercentage += CalculatePhaseProgress(pContext, 0, 15);
1501 dwPercentage += CalculatePhaseProgress(pContext, 1, 80);
1502 dwPercentage += CalculatePhaseProgress(pContext, 2, 5);
1503 dwPercentage = min(dwPercentage, 100); // ensure the percentage never goes over 100%.
1504
1505 if (pContext->fRollback)
1506 {
1507 dwPercentage = 100 - dwPercentage;
1508 }
1509
1510 //if (qwTotal) // avoid "divide by zero" if the MSI range is blank.
1511 //{
1512 // // calculate gauge.
1513 // double dProgressGauge = static_cast<double>(qwCompleted) / static_cast<double>(qwTotal);
1514 // dProgressGauge = (1.0 / (1.0 + exp(3.7 - dProgressGauge * 7.5)) - 0.024127021417669196) / 0.975872978582330804;
1515 // qwCompleted = (DWORD)(dProgressGauge * qwTotal);
1516
1517 // // calculate progress within range
1518 // //qwProgressComplete = (DWORD64)(dwMsiProgressComplete * (dProgressStageTotal / dwMsiProgressTotal));
1519 // //qwProgressComplete = min(qwProgressComplete, pEngineInfo->qwProgressStageTotal);
1520 //}
1521
1522#ifdef _DEBUG
1523 DWORD64 qwCompleted = pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwCompleted;
1524 DWORD64 qwTotal = pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwTotal;
1525 Trace(REPORT_STANDARD, "MSI progress: %I64u/%I64u (%u%%)", qwCompleted, qwTotal, dwPercentage);
1526 //AssertSz(qwCompleted <= qwTotal, "Completed progress is larger than total progress.");
1527#endif
1528
1529 message.type = WIU_MSI_EXECUTE_MESSAGE_PROGRESS;
1530 message.dwAllowedResults = MB_OKCANCEL;
1531 message.progress.dwPercentage = dwPercentage;
1532 nResult = pContext->pfnMessageHandler(&message, pContext->pvContext);
1533
1534 return nResult;
1535}
1536
1537static void ResetProgress(
1538 __in WIU_MSI_EXECUTE_CONTEXT* pContext
1539 )
1540{
1541 memset(pContext->rgMsiProgress, 0, sizeof(pContext->rgMsiProgress));
1542 pContext->dwCurrentProgressIndex = WIU_MSI_PROGRESS_INVALID;
1543}
1544
1545static DWORD CalculatePhaseProgress(
1546 __in WIU_MSI_EXECUTE_CONTEXT* pContext,
1547 __in DWORD dwProgressIndex,
1548 __in DWORD dwWeightPercentage
1549 )
1550{
1551 DWORD dwPhasePercentage = 0;
1552
1553 // If we've already passed this progress index, return the maximum percentage possible (the weight)
1554 if (dwProgressIndex < pContext->dwCurrentProgressIndex)
1555 {
1556 dwPhasePercentage = dwWeightPercentage;
1557 }
1558 else if (dwProgressIndex == pContext->dwCurrentProgressIndex) // have to do the math for the current progress.
1559 {
1560 WIU_MSI_PROGRESS* pProgress = pContext->rgMsiProgress + dwProgressIndex;
1561 if (pProgress->dwTotal)
1562 {
1563 DWORD64 dw64Completed = pProgress->dwCompleted;
1564 dwPhasePercentage = static_cast<DWORD>(dw64Completed * dwWeightPercentage / pProgress->dwTotal);
1565 }
1566 }
1567 // else we're not there yet so it has to be zero.
1568
1569 return dwPhasePercentage;
1570}
1571
1572void InitializeMessageData(
1573 __in_opt MSIHANDLE hRecord,
1574 __deref_out_ecount(*pcData) LPWSTR** prgsczData,
1575 __out DWORD* pcData
1576 )
1577{
1578 DWORD cData = 0;
1579 LPWSTR* rgsczData = NULL;
1580
1581 // If we have a record based message, try to get the extra data.
1582 if (hRecord)
1583 {
1584 cData = ::MsiRecordGetFieldCount(hRecord);
1585 if (cData)
1586 {
1587 rgsczData = (LPWSTR*)MemAlloc(sizeof(LPWSTR*) * cData, TRUE);
1588 }
1589
1590 for (DWORD i = 0; rgsczData && i < cData; ++i)
1591 {
1592 DWORD cch = 0;
1593
1594 // get string from record
1595#pragma prefast(push)
1596#pragma prefast(disable:6298)
1597 DWORD er = ::MsiRecordGetStringW(hRecord, i + 1, L"", &cch);
1598#pragma prefast(pop)
1599 if (ERROR_MORE_DATA == er)
1600 {
1601 HRESULT hr = StrAlloc(&rgsczData[i], ++cch);
1602 if (SUCCEEDED(hr))
1603 {
1604 er = ::MsiRecordGetStringW(hRecord, i + 1, rgsczData[i], &cch);
1605 }
1606 }
1607 }
1608 }
1609
1610 *prgsczData = rgsczData;
1611 *pcData = cData;
1612}
1613
1614void UninitializeMessageData(
1615 __in LPWSTR* rgsczData,
1616 __in DWORD cData
1617 )
1618{
1619 // Clean up if there was any data allocated.
1620 if (rgsczData)
1621 {
1622 for (DWORD i = 0; i < cData; ++i)
1623 {
1624 ReleaseStr(rgsczData[i]);
1625 }
1626
1627 MemFree(rgsczData);
1628 }
1629}
diff --git a/src/libs/dutil/WixToolset.DUtil/wuautil.cpp b/src/libs/dutil/WixToolset.DUtil/wuautil.cpp
new file mode 100644
index 00000000..dfb28818
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/wuautil.cpp
@@ -0,0 +1,104 @@
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// Exit macros
7#define WuaExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_WUAUTIL, x, s, __VA_ARGS__)
8#define WuaExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_WUAUTIL, x, s, __VA_ARGS__)
9#define WuaExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_WUAUTIL, x, s, __VA_ARGS__)
10#define WuaExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_WUAUTIL, x, s, __VA_ARGS__)
11#define WuaExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_WUAUTIL, x, s, __VA_ARGS__)
12#define WuaExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_WUAUTIL, x, s, __VA_ARGS__)
13#define WuaExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_WUAUTIL, p, x, e, s, __VA_ARGS__)
14#define WuaExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_WUAUTIL, p, x, s, __VA_ARGS__)
15#define WuaExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_WUAUTIL, p, x, e, s, __VA_ARGS__)
16#define WuaExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_WUAUTIL, p, x, s, __VA_ARGS__)
17#define WuaExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_WUAUTIL, e, x, s, __VA_ARGS__)
18#define WuaExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_WUAUTIL, g, x, s, __VA_ARGS__)
19
20
21// internal function declarations
22
23static HRESULT GetAutomaticUpdatesService(
24 __out IAutomaticUpdates **ppAutomaticUpdates
25 );
26
27
28// function definitions
29
30extern "C" HRESULT DAPI WuaPauseAutomaticUpdates()
31{
32 HRESULT hr = S_OK;
33 IAutomaticUpdates *pAutomaticUpdates = NULL;
34
35 hr = GetAutomaticUpdatesService(&pAutomaticUpdates);
36 WuaExitOnFailure(hr, "Failed to get the Automatic Updates service.");
37
38 hr = pAutomaticUpdates->Pause();
39 WuaExitOnFailure(hr, "Failed to pause the Automatic Updates service.");
40
41LExit:
42 ReleaseObject(pAutomaticUpdates);
43
44 return hr;
45}
46
47extern "C" HRESULT DAPI WuaResumeAutomaticUpdates()
48{
49 HRESULT hr = S_OK;
50 IAutomaticUpdates *pAutomaticUpdates = NULL;
51
52 hr = GetAutomaticUpdatesService(&pAutomaticUpdates);
53 WuaExitOnFailure(hr, "Failed to get the Automatic Updates service.");
54
55 hr = pAutomaticUpdates->Resume();
56 WuaExitOnFailure(hr, "Failed to resume the Automatic Updates service.");
57
58LExit:
59 ReleaseObject(pAutomaticUpdates);
60
61 return hr;
62}
63
64extern "C" HRESULT DAPI WuaRestartRequired(
65 __out BOOL* pfRestartRequired
66 )
67{
68 HRESULT hr = S_OK;
69 ISystemInformation* pSystemInformation = NULL;
70 VARIANT_BOOL bRestartRequired;
71
72 hr = ::CoCreateInstance(__uuidof(SystemInformation), NULL, CLSCTX_INPROC_SERVER, __uuidof(ISystemInformation), reinterpret_cast<LPVOID*>(&pSystemInformation));
73 WuaExitOnRootFailure(hr, "Failed to get WUA system information interface.");
74
75 hr = pSystemInformation->get_RebootRequired(&bRestartRequired);
76 WuaExitOnRootFailure(hr, "Failed to determine if restart is required from WUA.");
77
78 *pfRestartRequired = (VARIANT_FALSE != bRestartRequired);
79
80LExit:
81 ReleaseObject(pSystemInformation);
82
83 return hr;
84}
85
86
87// internal function definitions
88
89static HRESULT GetAutomaticUpdatesService(
90 __out IAutomaticUpdates **ppAutomaticUpdates
91 )
92{
93 HRESULT hr = S_OK;
94 CLSID clsidAutomaticUpdates = { };
95
96 hr = ::CLSIDFromProgID(L"Microsoft.Update.AutoUpdate", &clsidAutomaticUpdates);
97 WuaExitOnFailure(hr, "Failed to get CLSID for Microsoft.Update.AutoUpdate.");
98
99 hr = ::CoCreateInstance(clsidAutomaticUpdates, NULL, CLSCTX_INPROC_SERVER, IID_IAutomaticUpdates, reinterpret_cast<LPVOID*>(ppAutomaticUpdates));
100 WuaExitOnFailure(hr, "Failed to create instance of Microsoft.Update.AutoUpdate.");
101
102LExit:
103 return hr;
104}
diff --git a/src/libs/dutil/WixToolset.DUtil/xmlutil.cpp b/src/libs/dutil/WixToolset.DUtil/xmlutil.cpp
new file mode 100644
index 00000000..0f1e611d
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/xmlutil.cpp
@@ -0,0 +1,1332 @@
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// Exit macros
7#define XmlExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_XMLUTIL, x, s, __VA_ARGS__)
8#define XmlExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_XMLUTIL, x, s, __VA_ARGS__)
9#define XmlExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_XMLUTIL, x, s, __VA_ARGS__)
10#define XmlExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_XMLUTIL, x, s, __VA_ARGS__)
11#define XmlExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_XMLUTIL, x, s, __VA_ARGS__)
12#define XmlExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_XMLUTIL, x, s, __VA_ARGS__)
13#define XmlExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_XMLUTIL, p, x, e, s, __VA_ARGS__)
14#define XmlExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_XMLUTIL, p, x, s, __VA_ARGS__)
15#define XmlExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_XMLUTIL, p, x, e, s, __VA_ARGS__)
16#define XmlExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_XMLUTIL, p, x, s, __VA_ARGS__)
17#define XmlExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_XMLUTIL, e, x, s, __VA_ARGS__)
18#define XmlExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_XMLUTIL, g, x, s, __VA_ARGS__)
19
20// intialization globals
21CLSID vclsidXMLDOM = { 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, 0} };
22static volatile LONG vcXmlInitialized = 0;
23static BOOL vfMsxml40 = FALSE;
24static BOOL fComInitialized = FALSE;
25BOOL vfMsxml30 = FALSE;
26
27/********************************************************************
28 XmlInitialize - finds an appropriate version of the XML DOM
29
30*********************************************************************/
31extern "C" HRESULT DAPI XmlInitialize(
32 )
33{
34 HRESULT hr = S_OK;
35
36 if (!fComInitialized)
37 {
38 hr = ::CoInitialize(0);
39 if (RPC_E_CHANGED_MODE != hr)
40 {
41 XmlExitOnFailure(hr, "failed to initialize COM");
42 fComInitialized = TRUE;
43 }
44 }
45
46 LONG cInitialized = ::InterlockedIncrement(&vcXmlInitialized);
47 if (1 == cInitialized)
48 {
49 // NOTE: 4.0 behaves differently than 3.0 so there may be problems doing this
50#if 0
51 hr = ::CLSIDFromProgID(L"Msxml2.DOMDocument.4.0", &vclsidXMLDOM);
52 if (S_OK == hr)
53 {
54 vfMsxml40 = TRUE;
55 Trace(REPORT_VERBOSE, "found Msxml2.DOMDocument.4.0");
56 ExitFunction();
57 }
58#endif
59 hr = ::CLSIDFromProgID(L"Msxml2.DOMDocument", &vclsidXMLDOM);
60 if (FAILED(hr))
61 {
62 // try to fall back to old MSXML
63 hr = ::CLSIDFromProgID(L"MSXML.DOMDocument", &vclsidXMLDOM);
64 }
65 XmlExitOnFailure(hr, "failed to get CLSID for XML DOM");
66
67 Assert(IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument) ||
68 IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument20) ||
69 IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument26) ||
70 IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument30) ||
71 IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument40) ||
72 IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument50) ||
73 IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument60));
74 }
75
76 hr = S_OK;
77LExit:
78 return hr;
79}
80
81
82/********************************************************************
83 XmUninitialize -
84
85*********************************************************************/
86extern "C" void DAPI XmlUninitialize(
87 )
88{
89 AssertSz(vcXmlInitialized, "XmlUninitialize called when not initialized");
90
91 LONG cInitialized = ::InterlockedDecrement(&vcXmlInitialized);
92
93 if (0 == cInitialized)
94 {
95 memset(&vclsidXMLDOM, 0, sizeof(vclsidXMLDOM));
96
97 if (fComInitialized)
98 {
99 ::CoUninitialize();
100 }
101 }
102}
103
104extern "C" HRESULT DAPI XmlCreateElement(
105 __in IXMLDOMDocument *pixdDocument,
106 __in_z LPCWSTR wzElementName,
107 __out IXMLDOMElement **ppixnElement
108 )
109{
110 if (!ppixnElement || !pixdDocument)
111 {
112 return E_INVALIDARG;
113 }
114
115 HRESULT hr = S_OK;
116 BSTR bstrElementName = ::SysAllocString(wzElementName);
117 XmlExitOnNull(bstrElementName, hr, E_OUTOFMEMORY, "failed SysAllocString");
118 hr = pixdDocument->createElement(bstrElementName, ppixnElement);
119LExit:
120 ReleaseBSTR(bstrElementName);
121 return hr;
122}
123
124
125/********************************************************************
126 XmlCreateDocument -
127
128*********************************************************************/
129extern "C" HRESULT DAPI XmlCreateDocument(
130 __in_opt LPCWSTR pwzElementName,
131 __out IXMLDOMDocument** ppixdDocument,
132 __out_opt IXMLDOMElement** ppixeRootElement
133 )
134{
135 HRESULT hr = S_OK;
136 BOOL (WINAPI *pfnDisableWow64)(__out PVOID* ) = NULL;
137 BOOLEAN (WINAPI *pfnEnableWow64)(__in BOOLEAN ) = NULL;
138 BOOL (WINAPI *pfnRevertWow64)(__in PVOID ) = NULL;
139 BOOL fWow64Available = FALSE;
140 void *pvWow64State = NULL;
141
142 // RELEASEME
143 IXMLDOMElement* pixeRootElement = NULL;
144 IXMLDOMDocument *pixdDocument = NULL;
145
146 // Test if we have access to the Wow64 API, and store the result in fWow64Available
147 HMODULE hKernel32 = ::GetModuleHandleA("kernel32.dll");
148 XmlExitOnNullWithLastError(hKernel32, hr, "failed to get handle to kernel32.dll");
149
150 // This will test if we have access to the Wow64 API
151 if (NULL != GetProcAddress(hKernel32, "IsWow64Process"))
152 {
153 pfnDisableWow64 = (BOOL (WINAPI *)(PVOID *))::GetProcAddress(hKernel32, "Wow64DisableWow64FsRedirection");
154 pfnEnableWow64 = (BOOLEAN (WINAPI *)(BOOLEAN))::GetProcAddress(hKernel32, "Wow64EnableWow64FsRedirection");
155 pfnRevertWow64 = (BOOL (WINAPI *)(PVOID))::GetProcAddress(hKernel32, "Wow64RevertWow64FsRedirection");
156
157 fWow64Available = pfnDisableWow64 && pfnEnableWow64 && pfnRevertWow64;
158 }
159
160 // create the top level XML document
161 AssertSz(vcXmlInitialized, "XmlInitialize() was not called");
162
163 // Enable Wow64 Redirection, if possible
164 if (fWow64Available)
165 {
166 // We want to enable Wow64 redirection, but the Wow64 API requires us to disable it first to get its current state (so we can revert it later)
167 pfnDisableWow64(&pvWow64State);
168 // If we fail to enable it, don't bother trying to disable it later on
169 fWow64Available = pfnEnableWow64(TRUE);
170 }
171
172 hr = ::CoCreateInstance(vclsidXMLDOM, NULL, CLSCTX_INPROC_SERVER, XmlUtil_IID_IXMLDOMDocument, (void**)&pixdDocument);
173 XmlExitOnFailure(hr, "failed to create XML DOM Document");
174 Assert(pixdDocument);
175
176 if (IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument30) || IsEqualCLSID(vclsidXMLDOM, XmlUtil_CLSID_DOMDocument20))
177 {
178 vfMsxml30 = TRUE;
179 }
180
181 if (pwzElementName)
182 {
183 hr = XmlCreateElement(pixdDocument, pwzElementName, &pixeRootElement);
184 XmlExitOnFailure(hr, "failed XmlCreateElement");
185 hr = pixdDocument->appendChild(pixeRootElement, NULL);
186 XmlExitOnFailure(hr, "failed appendChild");
187 }
188
189 *ppixdDocument = pixdDocument;
190 pixdDocument = NULL;
191
192 if (ppixeRootElement)
193 {
194 *ppixeRootElement = pixeRootElement;
195 pixeRootElement = NULL;
196 }
197
198LExit:
199 // Re-disable Wow64 Redirection, if appropriate
200 if (fWow64Available && !pfnRevertWow64(pvWow64State))
201 {
202 // If we expected to be able to revert, and couldn't, fail in the only graceful way we can
203 ::ExitProcess(1);
204 }
205
206 ReleaseObject(pixeRootElement);
207 ReleaseObject(pixdDocument);
208 return hr;
209}
210
211
212/********************************************************************
213 XmlLoadDocument -
214
215*********************************************************************/
216extern "C" HRESULT DAPI XmlLoadDocument(
217 __in_z LPCWSTR wzDocument,
218 __out IXMLDOMDocument** ppixdDocument
219 )
220{
221 return XmlLoadDocumentEx(wzDocument, 0, ppixdDocument);
222}
223
224
225/********************************************************************
226 XmlReportParseError -
227
228*********************************************************************/
229static void XmlReportParseError(
230 __in IXMLDOMParseError* pixpe
231 )
232{
233 HRESULT hr = S_OK;
234 long lNumber = 0;
235 BSTR bstr = NULL;
236
237 Trace(REPORT_STANDARD, "Failed to parse XML. IXMLDOMParseError reports:");
238
239 hr = pixpe->get_errorCode(&lNumber);
240 XmlExitOnFailure(hr, "Failed to query IXMLDOMParseError.errorCode.");
241 Trace(REPORT_STANDARD, "errorCode = 0x%x", lNumber);
242
243 hr = pixpe->get_filepos(&lNumber);
244 XmlExitOnFailure(hr, "Failed to query IXMLDOMParseError.filepos.");
245 Trace(REPORT_STANDARD, "filepos = %d", lNumber);
246
247 hr = pixpe->get_line(&lNumber);
248 XmlExitOnFailure(hr, "Failed to query IXMLDOMParseError.line.");
249 Trace(REPORT_STANDARD, "line = %d", lNumber);
250
251 hr = pixpe->get_linepos(&lNumber);
252 XmlExitOnFailure(hr, "Failed to query IXMLDOMParseError.linepos.");
253 Trace(REPORT_STANDARD, "linepos = %d", lNumber);
254
255 hr = pixpe->get_reason(&bstr);
256 XmlExitOnFailure(hr, "Failed to query IXMLDOMParseError.reason.");
257 Trace(REPORT_STANDARD, "reason = %ls", bstr);
258 ReleaseNullBSTR(bstr);
259
260 hr = pixpe->get_srcText (&bstr);
261 XmlExitOnFailure(hr, "Failed to query IXMLDOMParseError.srcText .");
262 Trace(REPORT_STANDARD, "srcText = %ls", bstr);
263 ReleaseNullBSTR(bstr);
264
265LExit:
266 ReleaseBSTR(bstr);
267}
268
269/********************************************************************
270 XmlLoadDocumentEx -
271
272*********************************************************************/
273extern "C" HRESULT DAPI XmlLoadDocumentEx(
274 __in_z LPCWSTR wzDocument,
275 __in DWORD dwAttributes,
276 __out IXMLDOMDocument** ppixdDocument
277 )
278{
279 HRESULT hr = S_OK;
280 VARIANT_BOOL vbSuccess = 0;
281
282 // RELEASEME
283 IXMLDOMDocument* pixd = NULL;
284 IXMLDOMParseError* pixpe = NULL;
285 BSTR bstrLoad = NULL;
286
287 if (!wzDocument || !*wzDocument)
288 {
289 hr = E_UNEXPECTED;
290 XmlExitOnFailure(hr, "string must be non-null");
291 }
292
293 hr = XmlCreateDocument(NULL, &pixd);
294 if (hr == S_FALSE)
295 {
296 hr = E_FAIL;
297 }
298 XmlExitOnFailure(hr, "failed XmlCreateDocument");
299
300 if (dwAttributes & XML_LOAD_PRESERVE_WHITESPACE)
301 {
302 hr = pixd->put_preserveWhiteSpace(VARIANT_TRUE);
303 XmlExitOnFailure(hr, "failed put_preserveWhiteSpace");
304 }
305
306 // Security issue. Avoid triggering anything external.
307 hr = pixd->put_validateOnParse(VARIANT_FALSE);
308 XmlExitOnFailure(hr, "failed put_validateOnParse");
309 hr = pixd->put_resolveExternals(VARIANT_FALSE);
310 XmlExitOnFailure(hr, "failed put_resolveExternals");
311
312 bstrLoad = ::SysAllocString(wzDocument);
313 XmlExitOnNull(bstrLoad, hr, E_OUTOFMEMORY, "failed to allocate bstr for Load in XmlLoadDocumentEx");
314
315 hr = pixd->loadXML(bstrLoad, &vbSuccess);
316 if (S_FALSE == hr)
317 {
318 hr = HRESULT_FROM_WIN32(ERROR_OPEN_FAILED);
319 }
320
321 if (FAILED(hr) && S_OK == pixd->get_parseError(&pixpe))
322 {
323 XmlReportParseError(pixpe);
324 }
325
326 XmlExitOnFailure(hr, "failed loadXML");
327
328
329 hr = S_OK;
330LExit:
331 if (ppixdDocument)
332 {
333 *ppixdDocument = pixd;
334 pixd = NULL;
335 }
336 ReleaseBSTR(bstrLoad);
337 ReleaseObject(pixd);
338 ReleaseObject(pixpe);
339
340 return hr;
341}
342
343
344/*******************************************************************
345 XmlLoadDocumentFromFile
346
347********************************************************************/
348extern "C" HRESULT DAPI XmlLoadDocumentFromFile(
349 __in_z LPCWSTR wzPath,
350 __out IXMLDOMDocument** ppixdDocument
351 )
352{
353 return XmlLoadDocumentFromFileEx(wzPath, 0, ppixdDocument);
354}
355
356
357/*******************************************************************
358 XmlLoadDocumentFromFileEx
359
360********************************************************************/
361extern "C" HRESULT DAPI XmlLoadDocumentFromFileEx(
362 __in_z LPCWSTR wzPath,
363 __in DWORD dwAttributes,
364 __out IXMLDOMDocument** ppixdDocument
365 )
366{
367 HRESULT hr = S_OK;
368 VARIANT varPath;
369 VARIANT_BOOL vbSuccess = 0;
370
371 IXMLDOMDocument* pixd = NULL;
372 IXMLDOMParseError* pixpe = NULL;
373
374 ::VariantInit(&varPath);
375 varPath.vt = VT_BSTR;
376 varPath.bstrVal = ::SysAllocString(wzPath);
377 XmlExitOnNull(varPath.bstrVal, hr, E_OUTOFMEMORY, "failed to allocate bstr for Path in XmlLoadDocumentFromFileEx");
378
379 hr = XmlCreateDocument(NULL, &pixd);
380 if (hr == S_FALSE)
381 {
382 hr = E_FAIL;
383 }
384 XmlExitOnFailure(hr, "failed XmlCreateDocument");
385
386 if (dwAttributes & XML_LOAD_PRESERVE_WHITESPACE)
387 {
388 hr = pixd->put_preserveWhiteSpace(VARIANT_TRUE);
389 XmlExitOnFailure(hr, "failed put_preserveWhiteSpace");
390 }
391
392 // Avoid triggering anything external.
393 hr = pixd->put_validateOnParse(VARIANT_FALSE);
394 XmlExitOnFailure(hr, "failed put_validateOnParse");
395 hr = pixd->put_resolveExternals(VARIANT_FALSE);
396 XmlExitOnFailure(hr, "failed put_resolveExternals");
397
398 pixd->put_async(VARIANT_FALSE);
399 hr = pixd->load(varPath, &vbSuccess);
400 if (S_FALSE == hr)
401 {
402 hr = HRESULT_FROM_WIN32(ERROR_OPEN_FAILED);
403 }
404
405 if (FAILED(hr) && S_OK == pixd->get_parseError(&pixpe))
406 {
407 XmlReportParseError(pixpe);
408 }
409
410 XmlExitOnFailure(hr, "failed to load XML from: %ls", wzPath);
411
412 if (ppixdDocument)
413 {
414 *ppixdDocument = pixd;
415 pixd = NULL;
416 }
417
418 hr = S_OK;
419LExit:
420 ReleaseVariant(varPath);
421 ReleaseObject(pixd);
422 ReleaseObject(pixpe);
423
424 return hr;
425}
426
427
428/********************************************************************
429 XmlLoadDocumentFromBuffer
430
431*********************************************************************/
432extern "C" HRESULT DAPI XmlLoadDocumentFromBuffer(
433 __in_bcount(cbSource) const BYTE* pbSource,
434 __in SIZE_T cbSource,
435 __out IXMLDOMDocument** ppixdDocument
436 )
437{
438 HRESULT hr = S_OK;
439 IXMLDOMDocument* pixdDocument = NULL;
440 SAFEARRAY sa = { };
441 VARIANT vtXmlSource;
442 VARIANT_BOOL vbSuccess = 0;
443
444 ::VariantInit(&vtXmlSource);
445
446 // create document
447 hr = XmlCreateDocument(NULL, &pixdDocument);
448 if (hr == S_FALSE)
449 {
450 hr = E_FAIL;
451 }
452 XmlExitOnFailure(hr, "failed XmlCreateDocument");
453
454 // Security issue. Avoid triggering anything external.
455 hr = pixdDocument->put_validateOnParse(VARIANT_FALSE);
456 XmlExitOnFailure(hr, "failed put_validateOnParse");
457 hr = pixdDocument->put_resolveExternals(VARIANT_FALSE);
458 XmlExitOnFailure(hr, "failed put_resolveExternals");
459
460 // load document
461 sa.cDims = 1;
462 sa.fFeatures = FADF_STATIC | FADF_FIXEDSIZE;
463 sa.cbElements = 1;
464 sa.pvData = (PVOID)pbSource;
465 sa.rgsabound[0].cElements = (ULONG)cbSource;
466 vtXmlSource.vt = VT_ARRAY | VT_UI1;
467 vtXmlSource.parray = &sa;
468
469 hr = pixdDocument->load(vtXmlSource, &vbSuccess);
470 if (S_FALSE == hr)
471 {
472 hr = HRESULT_FROM_WIN32(ERROR_OPEN_FAILED);
473 }
474 XmlExitOnFailure(hr, "failed loadXML");
475
476 // return value
477 *ppixdDocument = pixdDocument;
478 pixdDocument = NULL;
479
480LExit:
481 ReleaseObject(pixdDocument);
482 return hr;
483}
484
485
486/********************************************************************
487 XmlSetAttribute -
488
489*********************************************************************/
490extern "C" HRESULT DAPI XmlSetAttribute(
491 __in IXMLDOMNode* pixnNode,
492 __in_z LPCWSTR pwzAttribute,
493 __in_z LPCWSTR pwzAttributeValue
494 )
495{
496 HRESULT hr = S_OK;
497 VARIANT varAttributeValue;
498 ::VariantInit(&varAttributeValue);
499
500 // RELEASEME
501 IXMLDOMDocument* pixdDocument = NULL;
502 IXMLDOMNamedNodeMap* pixnnmAttributes = NULL;
503 IXMLDOMAttribute* pixaAttribute = NULL;
504 IXMLDOMNode* pixaNode = NULL;
505 BSTR bstrAttributeName = ::SysAllocString(pwzAttribute);
506 XmlExitOnNull(bstrAttributeName, hr, E_OUTOFMEMORY, "failed to allocate bstr for AttributeName in XmlSetAttribute");
507
508 hr = pixnNode->get_attributes(&pixnnmAttributes);
509 XmlExitOnFailure(hr, "failed get_attributes in XmlSetAttribute(%ls)", pwzAttribute);
510
511 hr = pixnNode->get_ownerDocument(&pixdDocument);
512 if (hr == S_FALSE)
513 {
514 hr = E_FAIL;
515 }
516 XmlExitOnFailure(hr, "failed get_ownerDocument in XmlSetAttribute");
517
518 hr = pixdDocument->createAttribute(bstrAttributeName, &pixaAttribute);
519 XmlExitOnFailure(hr, "failed createAttribute in XmlSetAttribute(%ls)", pwzAttribute);
520
521 varAttributeValue.vt = VT_BSTR;
522 varAttributeValue.bstrVal = ::SysAllocString(pwzAttributeValue);
523 if (!varAttributeValue.bstrVal)
524 {
525 hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY);
526 }
527 XmlExitOnFailure(hr, "failed SysAllocString in XmlSetAttribute");
528
529 hr = pixaAttribute->put_nodeValue(varAttributeValue);
530 XmlExitOnFailure(hr, "failed put_nodeValue in XmlSetAttribute(%ls)", pwzAttribute);
531
532 hr = pixnnmAttributes->setNamedItem(pixaAttribute, &pixaNode);
533 XmlExitOnFailure(hr, "failed setNamedItem in XmlSetAttribute(%ls)", pwzAttribute);
534
535LExit:
536 ReleaseObject(pixdDocument);
537 ReleaseObject(pixnnmAttributes);
538 ReleaseObject(pixaAttribute);
539 ReleaseObject(pixaNode);
540 ReleaseBSTR(varAttributeValue.bstrVal);
541 ReleaseBSTR(bstrAttributeName);
542
543 return hr;
544}
545
546
547/********************************************************************
548 XmlSelectSingleNode -
549
550*********************************************************************/
551extern "C" HRESULT DAPI XmlSelectSingleNode(
552 __in IXMLDOMNode* pixnParent,
553 __in_z LPCWSTR wzXPath,
554 __out IXMLDOMNode **ppixnChild
555 )
556{
557 HRESULT hr = S_OK;
558
559 BSTR bstrXPath = NULL;
560
561 XmlExitOnNull(pixnParent, hr, E_UNEXPECTED, "pixnParent parameter was null in XmlSelectSingleNode");
562 XmlExitOnNull(ppixnChild, hr, E_UNEXPECTED, "ppixnChild parameter was null in XmlSelectSingleNode");
563
564 bstrXPath = ::SysAllocString(wzXPath ? wzXPath : L"");
565 XmlExitOnNull(bstrXPath, hr, E_OUTOFMEMORY, "failed to allocate bstr for XPath expression in XmlSelectSingleNode");
566
567 hr = pixnParent->selectSingleNode(bstrXPath, ppixnChild);
568
569LExit:
570 ReleaseBSTR(bstrXPath);
571
572 return hr;
573}
574
575
576/********************************************************************
577 XmlCreateTextNode -
578
579*********************************************************************/
580extern "C" HRESULT DAPI XmlCreateTextNode(
581 __in IXMLDOMDocument *pixdDocument,
582 __in_z LPCWSTR wzText,
583 __out IXMLDOMText **ppixnTextNode
584 )
585{
586 if (!ppixnTextNode || !pixdDocument)
587 {
588 return E_INVALIDARG;
589 }
590
591 HRESULT hr = S_OK;
592 BSTR bstrText = ::SysAllocString(wzText);
593 XmlExitOnNull(bstrText, hr, E_OUTOFMEMORY, "failed SysAllocString");
594 hr = pixdDocument->createTextNode(bstrText, ppixnTextNode);
595LExit:
596 ReleaseBSTR(bstrText);
597
598 return hr;
599}
600
601
602/********************************************************************
603 XmlGetText
604
605*********************************************************************/
606extern "C" HRESULT DAPI XmlGetText(
607 __in IXMLDOMNode* pixnNode,
608 __deref_out_z BSTR* pbstrText
609 )
610{
611 return pixnNode->get_text(pbstrText);
612}
613
614
615/********************************************************************
616 XmlGetAttribute
617
618*********************************************************************/
619extern "C" HRESULT DAPI XmlGetAttribute(
620 __in IXMLDOMNode* pixnNode,
621 __in_z LPCWSTR pwzAttribute,
622 __deref_out_z BSTR* pbstrAttributeValue
623 )
624{
625 Assert(pixnNode);
626 HRESULT hr = S_OK;
627
628 // RELEASEME
629 IXMLDOMNamedNodeMap* pixnnmAttributes = NULL;
630 IXMLDOMNode* pixnAttribute = NULL;
631 VARIANT varAttributeValue;
632 BSTR bstrAttribute = SysAllocString(pwzAttribute);
633
634 // INIT
635 ::VariantInit(&varAttributeValue);
636
637 // get attribute value from source
638 hr = pixnNode->get_attributes(&pixnnmAttributes);
639 XmlExitOnFailure(hr, "failed get_attributes");
640
641 hr = XmlGetNamedItem(pixnnmAttributes, bstrAttribute, &pixnAttribute);
642 if (S_FALSE == hr)
643 {
644 // hr = E_FAIL;
645 ExitFunction();
646 }
647 XmlExitOnFailure(hr, "failed getNamedItem in XmlGetAttribute(%ls)", pwzAttribute);
648
649 hr = pixnAttribute->get_nodeValue(&varAttributeValue);
650 XmlExitOnFailure(hr, "failed get_nodeValue in XmlGetAttribute(%ls)", pwzAttribute);
651
652 // steal the BSTR from the VARIANT
653 if (S_OK == hr && pbstrAttributeValue)
654 {
655 *pbstrAttributeValue = varAttributeValue.bstrVal;
656 varAttributeValue.bstrVal = NULL;
657 }
658
659LExit:
660 ReleaseObject(pixnnmAttributes);
661 ReleaseObject(pixnAttribute);
662 ReleaseVariant(varAttributeValue);
663 ReleaseBSTR(bstrAttribute);
664
665 return hr;
666}
667
668
669/********************************************************************
670 XmlGetAttributeEx
671
672*********************************************************************/
673HRESULT DAPI XmlGetAttributeEx(
674 __in IXMLDOMNode* pixnNode,
675 __in_z LPCWSTR wzAttribute,
676 __deref_out_z LPWSTR* psczAttributeValue
677 )
678{
679 Assert(pixnNode);
680 HRESULT hr = S_OK;
681 IXMLDOMNamedNodeMap* pixnnmAttributes = NULL;
682 IXMLDOMNode* pixnAttribute = NULL;
683 VARIANT varAttributeValue;
684 BSTR bstrAttribute = NULL;
685
686 ::VariantInit(&varAttributeValue);
687
688 // get attribute value from source
689 hr = pixnNode->get_attributes(&pixnnmAttributes);
690 XmlExitOnFailure(hr, "Failed get_attributes.");
691
692 bstrAttribute = ::SysAllocString(wzAttribute);
693 XmlExitOnNull(bstrAttribute, hr, E_OUTOFMEMORY, "Failed to allocate attribute name BSTR.");
694
695 hr = XmlGetNamedItem(pixnnmAttributes, bstrAttribute, &pixnAttribute);
696 if (S_FALSE == hr)
697 {
698 ExitFunction1(hr = E_NOTFOUND);
699 }
700 XmlExitOnFailure(hr, "Failed getNamedItem in XmlGetAttribute(%ls)", wzAttribute);
701
702 hr = pixnAttribute->get_nodeValue(&varAttributeValue);
703 if (S_FALSE == hr)
704 {
705 ExitFunction1(hr = E_NOTFOUND);
706 }
707 XmlExitOnFailure(hr, "Failed get_nodeValue in XmlGetAttribute(%ls)", wzAttribute);
708
709 // copy value
710 hr = StrAllocString(psczAttributeValue, varAttributeValue.bstrVal, 0);
711 XmlExitOnFailure(hr, "Failed to copy attribute value.");
712
713LExit:
714 ReleaseObject(pixnnmAttributes);
715 ReleaseObject(pixnAttribute);
716 ReleaseVariant(varAttributeValue);
717 ReleaseBSTR(bstrAttribute);
718
719 return hr;
720}
721
722
723/********************************************************************
724 XmlGetYesNoAttribute
725
726*********************************************************************/
727HRESULT DAPI XmlGetYesNoAttribute(
728 __in IXMLDOMNode* pixnNode,
729 __in_z LPCWSTR wzAttribute,
730 __out BOOL* pfYes
731 )
732{
733 HRESULT hr = S_OK;
734 LPWSTR sczValue = NULL;
735
736 hr = XmlGetAttributeEx(pixnNode, wzAttribute, &sczValue);
737 if (E_NOTFOUND != hr)
738 {
739 XmlExitOnFailure(hr, "Failed to get attribute.");
740
741 *pfYes = CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczValue, -1, L"yes", -1);
742 }
743
744LExit:
745 ReleaseStr(sczValue);
746
747 return hr;
748}
749
750
751
752/********************************************************************
753 XmlGetAttributeNumber
754
755*********************************************************************/
756extern "C" HRESULT DAPI XmlGetAttributeNumber(
757 __in IXMLDOMNode* pixnNode,
758 __in_z LPCWSTR pwzAttribute,
759 __out DWORD* pdwValue
760 )
761{
762 HRESULT hr = XmlGetAttributeNumberBase(pixnNode, pwzAttribute, 10, pdwValue);
763 return hr;
764}
765
766
767/********************************************************************
768 XmlGetAttributeNumberBase
769
770*********************************************************************/
771extern "C" HRESULT DAPI XmlGetAttributeNumberBase(
772 __in IXMLDOMNode* pixnNode,
773 __in_z LPCWSTR pwzAttribute,
774 __in int nBase,
775 __out DWORD* pdwValue
776 )
777{
778 HRESULT hr = S_OK;
779 BSTR bstrPointer = NULL;
780
781 hr = XmlGetAttribute(pixnNode, pwzAttribute, &bstrPointer);
782 XmlExitOnFailure(hr, "Failed to get value from attribute.");
783
784 if (S_OK == hr)
785 {
786 *pdwValue = wcstoul(bstrPointer, NULL, nBase);
787 }
788
789LExit:
790 ReleaseBSTR(bstrPointer);
791 return hr;
792}
793
794
795/********************************************************************
796 XmlGetAttributeLargeNumber
797
798*********************************************************************/
799extern "C" HRESULT DAPI XmlGetAttributeLargeNumber(
800 __in IXMLDOMNode* pixnNode,
801 __in_z LPCWSTR pwzAttribute,
802 __out DWORD64* pdw64Value
803 )
804{
805 HRESULT hr = S_OK;
806 BSTR bstrValue = NULL;
807
808 hr = XmlGetAttribute(pixnNode, pwzAttribute, &bstrValue);
809 XmlExitOnFailure(hr, "failed XmlGetAttribute");
810
811 if (S_OK == hr)
812 {
813 LONGLONG ll = 0;
814 hr = StrStringToInt64(bstrValue, 0, &ll);
815 XmlExitOnFailure(hr, "Failed to treat attribute value as number.");
816
817 *pdw64Value = ll;
818 }
819 else
820 {
821 *pdw64Value = 0;
822 }
823
824LExit:
825 ReleaseBSTR(bstrValue);
826 return hr;
827}
828
829
830/********************************************************************
831 XmlGetNamedItem -
832
833*********************************************************************/
834extern "C" HRESULT DAPI XmlGetNamedItem(
835 __in IXMLDOMNamedNodeMap *pixnmAttributes,
836 __in_opt LPCWSTR wzName,
837 __out IXMLDOMNode **ppixnNamedItem
838 )
839{
840 if (!pixnmAttributes || !ppixnNamedItem)
841 {
842 return E_INVALIDARG;
843 }
844
845 HRESULT hr = S_OK;
846 BSTR bstrName = ::SysAllocString(wzName);
847 XmlExitOnNull(bstrName, hr, E_OUTOFMEMORY, "failed SysAllocString");
848
849 hr = pixnmAttributes->getNamedItem(bstrName, ppixnNamedItem);
850
851LExit:
852 ReleaseBSTR(bstrName);
853 return hr;
854}
855
856
857/********************************************************************
858 XmlSetText -
859
860*********************************************************************/
861extern "C" HRESULT DAPI XmlSetText(
862 __in IXMLDOMNode *pixnNode,
863 __in_z LPCWSTR pwzText
864 )
865{
866 Assert(pixnNode && pwzText);
867 HRESULT hr = S_OK;
868 DOMNodeType dnType;
869
870 // RELEASEME
871 IXMLDOMDocument* pixdDocument = NULL;
872 IXMLDOMNodeList* pixnlNodeList = NULL;
873 IXMLDOMNode* pixnChildNode = NULL;
874 IXMLDOMText* pixtTextNode = NULL;
875 VARIANT varText;
876
877 ::VariantInit(&varText);
878
879 // find the text node
880 hr = pixnNode->get_childNodes(&pixnlNodeList);
881 XmlExitOnFailure(hr, "failed to get child nodes");
882
883 while (S_OK == (hr = pixnlNodeList->nextNode(&pixnChildNode)))
884 {
885 hr = pixnChildNode->get_nodeType(&dnType);
886 XmlExitOnFailure(hr, "failed to get node type");
887
888 if (NODE_TEXT == dnType)
889 break;
890 ReleaseNullObject(pixnChildNode);
891 }
892 if (S_FALSE == hr)
893 {
894 hr = S_OK;
895 }
896
897 if (pixnChildNode)
898 {
899 varText.vt = VT_BSTR;
900 varText.bstrVal = ::SysAllocString(pwzText);
901 if (!varText.bstrVal)
902 {
903 hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY);
904 }
905 XmlExitOnFailure(hr, "failed SysAllocString in XmlSetText");
906
907 hr = pixnChildNode->put_nodeValue(varText);
908 XmlExitOnFailure(hr, "failed IXMLDOMNode::put_nodeValue");
909 }
910 else
911 {
912 hr = pixnNode->get_ownerDocument(&pixdDocument);
913 if (hr == S_FALSE)
914 {
915 hr = E_FAIL;
916 }
917 XmlExitOnFailure(hr, "failed get_ownerDocument in XmlSetAttribute");
918
919 hr = XmlCreateTextNode(pixdDocument, pwzText, &pixtTextNode);
920 XmlExitOnFailure(hr, "failed createTextNode in XmlSetText(%ls)", pwzText);
921
922 hr = pixnNode->appendChild(pixtTextNode, NULL);
923 XmlExitOnFailure(hr, "failed appendChild in XmlSetText(%ls)", pwzText);
924 }
925
926 hr = *pwzText ? S_OK : S_FALSE;
927
928LExit:
929 ReleaseObject(pixnlNodeList);
930 ReleaseObject(pixnChildNode);
931 ReleaseObject(pixdDocument);
932 ReleaseObject(pixtTextNode);
933 ReleaseVariant(varText);
934 return hr;
935}
936
937
938/********************************************************************
939 XmlSetTextNumber -
940
941*********************************************************************/
942extern "C" HRESULT DAPI XmlSetTextNumber(
943 __in IXMLDOMNode *pixnNode,
944 __in DWORD dwValue
945 )
946{
947 HRESULT hr = S_OK;
948 WCHAR wzValue[12];
949
950 hr = ::StringCchPrintfW(wzValue, countof(wzValue), L"%u", dwValue);
951 XmlExitOnFailure(hr, "Failed to format numeric value as string.");
952
953 hr = XmlSetText(pixnNode, wzValue);
954
955LExit:
956 return hr;
957}
958
959
960/********************************************************************
961 XmlCreateChild -
962
963*********************************************************************/
964extern "C" HRESULT DAPI XmlCreateChild(
965 __in IXMLDOMNode* pixnParent,
966 __in_z LPCWSTR pwzElementType,
967 __out IXMLDOMNode** ppixnChild
968 )
969{
970 HRESULT hr = S_OK;
971
972 // RELEASEME
973 IXMLDOMDocument* pixdDocument = NULL;
974 IXMLDOMNode* pixnChild = NULL;
975
976 hr = pixnParent->get_ownerDocument(&pixdDocument);
977 if (hr == S_FALSE)
978 {
979 hr = E_FAIL;
980 }
981 XmlExitOnFailure(hr, "failed get_ownerDocument");
982
983 hr = XmlCreateElement(pixdDocument, pwzElementType, (IXMLDOMElement**) &pixnChild);
984 if (hr == S_FALSE)
985 {
986 hr = E_FAIL;
987 }
988 XmlExitOnFailure(hr, "failed createElement");
989
990 pixnParent->appendChild(pixnChild,NULL);
991 if (hr == S_FALSE)
992 {
993 hr = E_FAIL;
994 }
995 XmlExitOnFailure(hr, "failed appendChild");
996
997 if (ppixnChild)
998 {
999 *ppixnChild = pixnChild;
1000 pixnChild = NULL;
1001 }
1002
1003LExit:
1004 ReleaseObject(pixdDocument);
1005 ReleaseObject(pixnChild);
1006 return hr;
1007}
1008
1009/********************************************************************
1010 XmlRemoveAttribute -
1011
1012*********************************************************************/
1013extern "C" HRESULT DAPI XmlRemoveAttribute(
1014 __in IXMLDOMNode* pixnNode,
1015 __in_z LPCWSTR pwzAttribute
1016 )
1017{
1018 HRESULT hr = S_OK;
1019
1020 // RELEASEME
1021 IXMLDOMNamedNodeMap* pixnnmAttributes = NULL;
1022 BSTR bstrAttribute = ::SysAllocString(pwzAttribute);
1023 XmlExitOnNull(bstrAttribute, hr, E_OUTOFMEMORY, "failed to allocate bstr for attribute in XmlRemoveAttribute");
1024
1025 hr = pixnNode->get_attributes(&pixnnmAttributes);
1026 XmlExitOnFailure(hr, "failed get_attributes in RemoveXmlAttribute(%ls)", pwzAttribute);
1027
1028 hr = pixnnmAttributes->removeNamedItem(bstrAttribute, NULL);
1029 XmlExitOnFailure(hr, "failed removeNamedItem in RemoveXmlAttribute(%ls)", pwzAttribute);
1030
1031LExit:
1032 ReleaseObject(pixnnmAttributes);
1033 ReleaseBSTR(bstrAttribute);
1034
1035 return hr;
1036}
1037
1038
1039/********************************************************************
1040 XmlSelectNodes -
1041
1042*********************************************************************/
1043extern "C" HRESULT DAPI XmlSelectNodes(
1044 __in IXMLDOMNode* pixnParent,
1045 __in_z LPCWSTR wzXPath,
1046 __out IXMLDOMNodeList **ppixnlChildren
1047 )
1048{
1049 HRESULT hr = S_OK;
1050
1051 BSTR bstrXPath = NULL;
1052
1053 XmlExitOnNull(pixnParent, hr, E_UNEXPECTED, "pixnParent parameter was null in XmlSelectNodes");
1054 XmlExitOnNull(ppixnlChildren, hr, E_UNEXPECTED, "ppixnChild parameter was null in XmlSelectNodes");
1055
1056 bstrXPath = ::SysAllocString(wzXPath ? wzXPath : L"");
1057 XmlExitOnNull(bstrXPath, hr, E_OUTOFMEMORY, "failed to allocate bstr for XPath expression in XmlSelectNodes");
1058
1059 hr = pixnParent->selectNodes(bstrXPath, ppixnlChildren);
1060
1061LExit:
1062 ReleaseBSTR(bstrXPath);
1063 return hr;
1064}
1065
1066
1067/********************************************************************
1068 XmlNextAttribute - returns the next attribute in a node list
1069
1070 NOTE: pbstrAttribute is optional
1071 returns S_OK if found an element
1072 returns S_FALSE if no element found
1073 returns E_* if something went wrong
1074********************************************************************/
1075extern "C" HRESULT DAPI XmlNextAttribute(
1076 __in IXMLDOMNamedNodeMap* pixnnm,
1077 __out IXMLDOMNode** pixnAttribute,
1078 __deref_opt_out_z_opt BSTR* pbstrAttribute
1079 )
1080{
1081 Assert(pixnnm && pixnAttribute);
1082
1083 HRESULT hr = S_OK;
1084 IXMLDOMNode* pixn = NULL;
1085 DOMNodeType nt;
1086
1087 // null out the return values
1088 *pixnAttribute = NULL;
1089 if (pbstrAttribute)
1090 {
1091 *pbstrAttribute = NULL;
1092 }
1093
1094 hr = pixnnm->nextNode(&pixn);
1095 XmlExitOnFailure(hr, "Failed to get next attribute.");
1096
1097 if (S_OK == hr)
1098 {
1099 hr = pixn->get_nodeType(&nt);
1100 XmlExitOnFailure(hr, "failed to get node type");
1101
1102 if (NODE_ATTRIBUTE != nt)
1103 {
1104 hr = E_UNEXPECTED;
1105 XmlExitOnFailure(hr, "Failed to get expected node type back: attribute");
1106 }
1107
1108 // if the caller asked for the attribute name
1109 if (pbstrAttribute)
1110 {
1111 hr = pixn->get_baseName(pbstrAttribute);
1112 XmlExitOnFailure(hr, "failed to get attribute name");
1113 }
1114
1115 *pixnAttribute = pixn;
1116 pixn = NULL;
1117 }
1118
1119LExit:
1120 ReleaseObject(pixn);
1121 return hr;
1122}
1123
1124
1125/********************************************************************
1126 XmlNextElement - returns the next element in a node list
1127
1128 NOTE: pbstrElement is optional
1129 returns S_OK if found an element
1130 returns S_FALSE if no element found
1131 returns E_* if something went wrong
1132********************************************************************/
1133extern "C" HRESULT DAPI XmlNextElement(
1134 __in IXMLDOMNodeList* pixnl,
1135 __out IXMLDOMNode** pixnElement,
1136 __deref_opt_out_z_opt BSTR* pbstrElement
1137 )
1138{
1139 Assert(pixnl && pixnElement);
1140
1141 HRESULT hr = S_OK;
1142 IXMLDOMNode* pixn = NULL;
1143 DOMNodeType nt;
1144
1145 // null out the return values
1146 *pixnElement = NULL;
1147 if (pbstrElement)
1148 {
1149 *pbstrElement = NULL;
1150 }
1151
1152 //
1153 // find the next element in the list
1154 //
1155 while (S_OK == (hr = pixnl->nextNode(&pixn)))
1156 {
1157 hr = pixn->get_nodeType(&nt);
1158 XmlExitOnFailure(hr, "failed to get node type");
1159
1160 if (NODE_ELEMENT == nt)
1161 break;
1162
1163 ReleaseNullObject(pixn);
1164 }
1165 XmlExitOnFailure(hr, "failed to get next element");
1166
1167 // if we have a node and the caller asked for the element name
1168 if (pixn && pbstrElement)
1169 {
1170 hr = pixn->get_baseName(pbstrElement);
1171 XmlExitOnFailure(hr, "failed to get element name");
1172 }
1173
1174 *pixnElement = pixn;
1175 pixn = NULL;
1176
1177 hr = *pixnElement ? S_OK : S_FALSE;
1178LExit:
1179 ReleaseObject(pixn);
1180 return hr;
1181}
1182
1183
1184/********************************************************************
1185 XmlRemoveChildren -
1186
1187*********************************************************************/
1188extern "C" HRESULT DAPI XmlRemoveChildren(
1189 __in IXMLDOMNode* pixnSource,
1190 __in_z LPCWSTR pwzXPath
1191 )
1192{
1193 HRESULT hr = S_OK;
1194
1195 // RELEASEME
1196 IXMLDOMNodeList* pixnlNodeList = NULL;
1197 IXMLDOMNode* pixnNode = NULL;
1198 IXMLDOMNode* pixnRemoveChild = NULL;
1199
1200 if (pwzXPath)
1201 {
1202 hr = XmlSelectNodes(pixnSource, pwzXPath, &pixnlNodeList);
1203 XmlExitOnFailure(hr, "failed XmlSelectNodes");
1204 }
1205 else
1206 {
1207 hr = pixnSource->get_childNodes(&pixnlNodeList);
1208 XmlExitOnFailure(hr, "failed childNodes");
1209 }
1210 if (S_FALSE == hr)
1211 {
1212 ExitFunction();
1213 }
1214
1215 while (S_OK == (hr = pixnlNodeList->nextNode(&pixnNode)))
1216 {
1217 hr = pixnSource->removeChild(pixnNode, &pixnRemoveChild);
1218 XmlExitOnFailure(hr, "failed removeChild");
1219
1220 ReleaseNullObject(pixnRemoveChild);
1221 ReleaseNullObject(pixnNode);
1222 }
1223 if (S_FALSE == hr)
1224 {
1225 hr = S_OK;
1226 }
1227
1228LExit:
1229 ReleaseObject(pixnlNodeList);
1230 ReleaseObject(pixnNode);
1231 ReleaseObject(pixnRemoveChild);
1232
1233 return hr;
1234}
1235
1236
1237/********************************************************************
1238 XmlSaveDocument -
1239
1240*********************************************************************/
1241extern "C" HRESULT DAPI XmlSaveDocument(
1242 __in IXMLDOMDocument* pixdDocument,
1243 __inout LPCWSTR wzPath
1244 )
1245{
1246 HRESULT hr = S_OK;
1247
1248 // RELEASEME
1249 VARIANT varsDestPath;
1250
1251 ::VariantInit(&varsDestPath);
1252 varsDestPath.vt = VT_BSTR;
1253 varsDestPath.bstrVal = ::SysAllocString(wzPath);
1254 if (!varsDestPath.bstrVal)
1255 {
1256 hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY);
1257 }
1258 XmlExitOnFailure(hr, "failed to create BSTR");
1259
1260 hr = pixdDocument->save(varsDestPath);
1261 if (hr == S_FALSE)
1262 {
1263 hr = E_FAIL;
1264 }
1265 XmlExitOnFailure(hr, "failed save in WriteDocument");
1266
1267LExit:
1268 ReleaseVariant(varsDestPath);
1269 return hr;
1270}
1271
1272
1273/********************************************************************
1274 XmlSaveDocumentToBuffer
1275
1276*********************************************************************/
1277extern "C" HRESULT DAPI XmlSaveDocumentToBuffer(
1278 __in IXMLDOMDocument* pixdDocument,
1279 __deref_out_bcount(*pcbDest) BYTE** ppbDest,
1280 __out DWORD* pcbDest
1281 )
1282{
1283 HRESULT hr = S_OK;
1284 IStream* pStream = NULL;
1285 LARGE_INTEGER li = { };
1286 STATSTG statstg = { };
1287 BYTE* pbDest = NULL;
1288 ULONG cbRead = 0;
1289 VARIANT vtDestination;
1290
1291 ::VariantInit(&vtDestination);
1292
1293 // create stream
1294 hr = ::CreateStreamOnHGlobal(NULL, TRUE, &pStream);
1295 XmlExitOnFailure(hr, "Failed to create stream.");
1296
1297 // write document to stream
1298 vtDestination.vt = VT_UNKNOWN;
1299 vtDestination.punkVal = (IUnknown*)pStream;
1300 hr = pixdDocument->save(vtDestination);
1301 XmlExitOnFailure(hr, "Failed to save document.");
1302
1303 // get stream size
1304 hr = pStream->Stat(&statstg, STATFLAG_NONAME);
1305 XmlExitOnFailure(hr, "Failed to get stream size.");
1306
1307 // allocate buffer
1308 pbDest = static_cast<BYTE*>(MemAlloc(statstg.cbSize.LowPart, TRUE));
1309 XmlExitOnNull(pbDest, hr, E_OUTOFMEMORY, "Failed to allocate destination buffer.");
1310
1311 // read data from stream
1312 li.QuadPart = 0;
1313 hr = pStream->Seek(li, STREAM_SEEK_SET, NULL);
1314 XmlExitOnFailure(hr, "Failed to seek stream.");
1315
1316 hr = pStream->Read(pbDest, statstg.cbSize.LowPart, &cbRead);
1317 if (cbRead < statstg.cbSize.LowPart)
1318 {
1319 hr = E_FAIL;
1320 }
1321 XmlExitOnFailure(hr, "Failed to read stream content to buffer.");
1322
1323 // return value
1324 *ppbDest = pbDest;
1325 pbDest = NULL;
1326 *pcbDest = statstg.cbSize.LowPart;
1327
1328LExit:
1329 ReleaseObject(pStream);
1330 ReleaseMem(pbDest);
1331 return hr;
1332}
diff --git a/src/libs/dutil/WixToolset.DUtil/xsd/thmutil.xsd b/src/libs/dutil/WixToolset.DUtil/xsd/thmutil.xsd
new file mode 100644
index 00000000..46c20e4a
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/xsd/thmutil.xsd
@@ -0,0 +1,1188 @@
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
5<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
6 xmlns:xse="http://wixtoolset.org/schemas/XmlSchemaExtension"
7 xmlns:html="http://www.w3.org/1999/xhtml"
8 targetNamespace="http://wixtoolset.org/schemas/v4/thmutil"
9 xmlns="http://wixtoolset.org/schemas/v4/thmutil">
10 <xs:annotation>
11 <xs:documentation>
12 Schema for describing Theme files processed by thmutil.
13 </xs:documentation>
14 </xs:annotation>
15
16 <xs:import namespace="http://www.w3.org/1999/xhtml" />
17
18 <xs:element name="Theme">
19 <xs:annotation>
20 <xs:documentation>
21 This is the top-level container element for every thmutil Theme file.
22 </xs:documentation>
23 </xs:annotation>
24 <xs:complexType>
25 <xs:sequence>
26 <xs:element ref="Font" maxOccurs="unbounded" />
27 <xs:element ref="Window" />
28 </xs:sequence>
29 <xs:attribute name="ImageFile" type="xs:string">
30 <xs:annotation>
31 <xs:documentation>
32 Relative path to an image file that can serve as a single source for images in the rest of the theme.
33 This image is referenced by controls using the SourceX and SourceY attributes.
34 Mutually exclusive with the ImageResource attribute.
35 </xs:documentation>
36 </xs:annotation>
37 </xs:attribute>
38 <xs:attribute name="ImageResource" type="xs:string">
39 <xs:annotation>
40 <xs:documentation>
41 Identifier that references an image resource in the module for the window.
42 Mutually exclusive with the ImageFile attribute.
43 </xs:documentation>
44 </xs:annotation>
45 </xs:attribute>
46 </xs:complexType>
47 </xs:element>
48
49 <xs:element name="Font">
50 <xs:annotation>
51 <xs:documentation>Defines a font including the size and color.</xs:documentation>
52 </xs:annotation>
53 <xs:complexType>
54 <xs:simpleContent>
55 <xs:extension base="xs:string">
56 <xs:annotation>
57 <xs:documentation>Name of the font face (required).</xs:documentation>
58 </xs:annotation>
59 <xs:attribute name="Id" type="xs:nonNegativeInteger" use="required">
60 <xs:annotation>
61 <xs:documentation>Numeric identifier for the font. Due to limitations in thmutil the first Font must start with "0" and each subsequent Font must increment the Id by 1. Failure to ensure the Font identifiers follow this strict ordering will create unexpected behavior or crashes.</xs:documentation>
62 </xs:annotation>
63 </xs:attribute>
64 <xs:attribute name="Height" type="xs:int" use="required">
65 <xs:annotation>
66 <xs:documentation>Font size. Use negative numbers to specify the font in pixels.</xs:documentation>
67 </xs:annotation>
68 </xs:attribute>
69 <xs:attribute name="Weight" type="xs:nonNegativeInteger">
70 <xs:annotation>
71 <xs:documentation>Font weight.</xs:documentation>
72 </xs:annotation>
73 </xs:attribute>
74 <xs:attribute name="Foreground" type="FontColorType">
75 <xs:annotation>
76 <xs:documentation>
77 A system color id or a hexadecimal value representing BGR foreground color of the font.
78 "ffffff" is white, "ff0000" is pure blue, "00ff00" is pure green, "0000ff" is pure red, and "000000" is black.
79 If this attribute is absent the foreground will be transparent.
80 Supported system color ids are: btnface, btntext, graytext, highlight, highlighttext, hotlight, window, and windowtext.
81 </xs:documentation>
82 </xs:annotation>
83 </xs:attribute>
84 <xs:attribute name="Background" type="FontColorType">
85 <xs:annotation>
86 <xs:documentation>
87 A system color id or a hexadecimal value representing BGR background color of the font.
88 "ffffff" is white, "ff0000" is pure blue, "00ff00" is pure green, "0000ff" is pure red, and "000000" is black.
89 If this attribute is absent the background will be transparent.
90 Supported system color ids are: btnface, btntext, graytext, highlight, highlighttext, hotlight, window, and windowtext.
91 </xs:documentation>
92 </xs:annotation>
93 </xs:attribute>
94 <xs:attribute name="Underline" type="YesNoType">
95 <xs:annotation>
96 <xs:documentation>Specifies whether the font is underlined.</xs:documentation>
97 </xs:annotation>
98 </xs:attribute>
99 </xs:extension>
100 </xs:simpleContent>
101 </xs:complexType>
102 </xs:element>
103
104 <xs:element name="ImageList">
105 <xs:annotation>
106 <xs:documentation>List of images which can be shared between multiple controls.</xs:documentation>
107 </xs:annotation>
108 <xs:complexType>
109 <xs:choice maxOccurs="unbounded">
110 <xs:element ref="Image" />
111 </xs:choice>
112 <xs:attribute name="Name" type="xs:string" use="required">
113 <xs:annotation>
114 <xs:documentation>
115 Name of the ImageList, to be referenced by other controls.
116 </xs:documentation>
117 </xs:annotation>
118 </xs:attribute>
119 </xs:complexType>
120 </xs:element>
121
122 <xs:element name="Page">
123 <xs:annotation>
124 <xs:documentation>Named set of controls that can be shown and hidden collectively.</xs:documentation>
125 </xs:annotation>
126 <xs:complexType>
127 <xs:group ref="ControlElements" maxOccurs="unbounded"/>
128 <xs:attribute name="Name" type="xs:string">
129 <xs:annotation>
130 <xs:documentation>
131 Optional name for the page.
132 </xs:documentation>
133 </xs:annotation>
134 </xs:attribute>
135 </xs:complexType>
136 </xs:element>
137
138 <xs:element name="Window">
139 <xs:annotation>
140 <xs:documentation>Defines the overall look of the main window.</xs:documentation>
141 </xs:annotation>
142 <xs:complexType>
143 <xs:choice minOccurs="0" maxOccurs="unbounded">
144 <xs:element ref="ImageList" />
145 <xs:element ref="Page" />
146 <xs:group ref="ControlElements" minOccurs="0" maxOccurs="unbounded" />
147 </xs:choice>
148 <xs:attribute name="AutoResize" type="YesNoType">
149 <xs:annotation>
150 <xs:documentation>Specifies whether the ThmUtil default window proc should process WM_SIZE and WM_SIZING events.</xs:documentation>
151 </xs:annotation>
152 </xs:attribute>
153 <xs:attribute name="Caption" type="xs:string">
154 <xs:annotation>
155 <xs:documentation>
156 Caption for the window.
157 This is required if not using the StringId attribute.
158 </xs:documentation>
159 </xs:annotation>
160 </xs:attribute>
161 <xs:attribute name="FontId" type="xs:nonNegativeInteger" use="required">
162 <xs:annotation>
163 <xs:documentation>Numeric identifier to the Font element that serves as the default font for the window.</xs:documentation>
164 </xs:annotation>
165 </xs:attribute>
166 <xs:attribute name="Height" type="xs:positiveInteger" use="required">
167 <xs:annotation>
168 <xs:documentation>Height of the window's client area.</xs:documentation>
169 </xs:annotation>
170 </xs:attribute>
171 <xs:attribute name="HexStyle" type="xs:hexBinary">
172 <xs:annotation>
173 <xs:documentation>
174 Hexadecimal window style. If this is not specified the default value is: WS_OVERLAPPED | WS_VISIBLE | WS_MINIMIZEBOX | WS_SYSMENU.
175 If SourceX and SourceY are specified, then WS_OVERLAPPED is replaced with WS_POPUP.
176 </xs:documentation>
177 </xs:annotation>
178 </xs:attribute>
179 <xs:attribute name="IconFile" type="xs:string">
180 <xs:annotation>
181 <xs:documentation>Relative path to an icon file for the window. Mutually exclusive with IconResource and SourceX and SourceY attributes.</xs:documentation>
182 </xs:annotation>
183 </xs:attribute>
184 <xs:attribute name="IconResource" type="xs:string">
185 <xs:annotation>
186 <xs:documentation>
187 Identifier that references an icon resource in the module for the icon for the window.
188 Mutually exclusive with IconFile and SourceX and SourceY attributes.
189 </xs:documentation>
190 </xs:annotation>
191 </xs:attribute>
192 <xs:attribute name="MinimumHeight" type="xs:positiveInteger">
193 <xs:annotation>
194 <xs:documentation>Minimum height of the window. Only functions if AutoResize is enabled.</xs:documentation>
195 </xs:annotation>
196 </xs:attribute>
197 <xs:attribute name="MinimumWidth" type="xs:positiveInteger">
198 <xs:annotation>
199 <xs:documentation>Minimum width of the window. Only functions if AutoResize is enabled.</xs:documentation>
200 </xs:annotation>
201 </xs:attribute>
202 <xs:attribute name="SourceX" type="xs:nonNegativeInteger">
203 <xs:annotation>
204 <xs:documentation>X offset of the window background in the Theme/@ImageFile. Mutually exclusive with IconFile and IconResource.</xs:documentation>
205 </xs:annotation>
206 </xs:attribute>
207 <xs:attribute name="SourceY" type="xs:nonNegativeInteger">
208 <xs:annotation>
209 <xs:documentation>Y offset of the window background in the Theme/@ImageFile. Mutually exclusive with IconFile and IconResource.</xs:documentation>
210 </xs:annotation>
211 </xs:attribute>
212 <xs:attribute name="StringId" type="xs:nonNegativeInteger">
213 <xs:annotation>
214 <xs:documentation>
215 Identifier that references a string resource in the module to define the window caption.
216 Mutually exclusive with the Caption attribute.
217 </xs:documentation>
218 </xs:annotation>
219 </xs:attribute>
220 <xs:attribute name="Width" type="xs:positiveInteger" use="required">
221 <xs:annotation>
222 <xs:documentation>Width of the window's client area.</xs:documentation>
223 </xs:annotation>
224 </xs:attribute>
225 </xs:complexType>
226 </xs:element>
227
228 <xs:element name="Billboard">
229 <xs:annotation>
230 <xs:documentation>Defines a control that rotates through a set of images on a specified interval.</xs:documentation>
231 </xs:annotation>
232 <xs:complexType>
233 <xs:sequence>
234 <xs:element ref="Image" />
235 </xs:sequence>
236 <xs:attributeGroup ref="CommonControlAttributes" />
237 <xs:attribute name="Interval" type="xs:positiveInteger">
238 <xs:annotation>
239 <xs:documentation>
240 Specifies the time to wait before showing the next image, in milliseconds.
241 </xs:documentation>
242 </xs:annotation>
243 </xs:attribute>
244 <xs:attribute name="Loop" type="YesNoType">
245 <xs:annotation>
246 <xs:documentation>Specifies whether the billboard should loop through the images infinitely.</xs:documentation>
247 </xs:annotation>
248 </xs:attribute>
249 </xs:complexType>
250 </xs:element>
251
252 <xs:element name="Button">
253 <xs:annotation>
254 <xs:documentation>Defines a button.</xs:documentation>
255 </xs:annotation>
256 <xs:complexType mixed="true">
257 <xs:annotation>
258 <xs:documentation>
259 Text to display in the button.
260 Mutually exclusive with the StringId attribute and child Text elements.
261 </xs:documentation>
262 </xs:annotation>
263 <xs:choice minOccurs="0" maxOccurs="unbounded">
264 <xs:annotation>
265 <xs:documentation>
266 If multiple Action elements are given, the conditions should be mutually exclusive (when multiple conditions are true, the behavior is undefined and could be changed at any time).
267 If none of the conditions of the Action elements are true, then it uses the Action element without the Condition attribute.
268 </xs:documentation>
269 </xs:annotation>
270 <xs:element ref="BrowseDirectoryAction" />
271 <xs:element ref="ChangePageAction" />
272 <xs:element ref="CloseWindowAction" />
273 <xs:element ref="Text" />
274 <xs:element ref="Tooltip" maxOccurs="1" />
275 </xs:choice>
276 <xs:attributeGroup ref="CommonControlAttributes" />
277 <xs:attribute name="FontId" type="xs:nonNegativeInteger" use="required">
278 <xs:annotation>
279 <xs:documentation>Numeric identifier to the Font element that serves as the font for the control. Only valid when using graphic buttons.</xs:documentation>
280 </xs:annotation>
281 </xs:attribute>
282 <xs:attribute name="HoverFontId" type="xs:nonNegativeInteger">
283 <xs:annotation>
284 <xs:documentation>Numeric identifier to the Font element that serves as the font when the control is hovered over. Only valid when using graphic buttons.</xs:documentation>
285 </xs:annotation>
286 </xs:attribute>
287 <xs:attribute name="ImageFile" type="xs:string">
288 <xs:annotation>
289 <xs:documentation>
290 Relative path to an image file to define a graphic button.
291 The image must be 4x the height to represent the button in 4 states: unselected, hover, selected, focused.
292 Mutually exclusive with ImageResource and SourceX and SourceY attributes.
293 </xs:documentation>
294 </xs:annotation>
295 </xs:attribute>
296 <xs:attribute name="ImageResource" type="xs:string">
297 <xs:annotation>
298 <xs:documentation>
299 Identifier that references an image resource in the module to define a graphic button.
300 The image must be 4x the height to represent the button in 4 states: unselected, hover, selected, focused.
301 Mutually exclusive with ImageFile and SourceX and SourceY attributes.
302 </xs:documentation>
303 </xs:annotation>
304 </xs:attribute>
305 <xs:attribute name="SelectedFontId" type="xs:nonNegativeInteger">
306 <xs:annotation>
307 <xs:documentation>Numeric identifier to the Font element that serves as the font when the control is selected. Only valid when using graphic buttons.</xs:documentation>
308 </xs:annotation>
309 </xs:attribute>
310 <xs:attribute name="StringId" type="xs:nonNegativeInteger">
311 <xs:annotation>
312 <xs:documentation>
313 Identifier that references a string resource in the module to define the text for the control.
314 </xs:documentation>
315 </xs:annotation>
316 </xs:attribute>
317 </xs:complexType>
318 </xs:element>
319
320 <xs:element name="BrowseDirectoryAction">
321 <xs:annotation>
322 <xs:documentation>
323 When the button is pressed, a directory browser dialog is shown.
324 </xs:documentation>
325 </xs:annotation>
326 <xs:complexType>
327 <xs:attribute name="Condition" type="xs:string">
328 <xs:annotation>
329 <xs:documentation>
330 The condition that determines if the parent control will execute this action.
331 </xs:documentation>
332 </xs:annotation>
333 </xs:attribute>
334 <xs:attribute name="VariableName" type="xs:string" use="required">
335 <xs:annotation>
336 <xs:documentation>
337 The name of the variable to update when the user selects a directory from the dialog.
338 </xs:documentation>
339 </xs:annotation>
340 </xs:attribute>
341 </xs:complexType>
342 </xs:element>
343
344 <xs:element name="ChangePageAction">
345 <xs:annotation>
346 <xs:documentation>
347 When the button is pressed, the specified page is shown.
348 </xs:documentation>
349 </xs:annotation>
350 <xs:complexType>
351 <xs:attribute name="Cancel" type="YesNoType">
352 <xs:annotation>
353 <xs:documentation>
354 When set to 'yes', none of the variable changes made on the current page are saved.
355 </xs:documentation>
356 </xs:annotation>
357 </xs:attribute>
358 <xs:attribute name="Condition" type="xs:string">
359 <xs:annotation>
360 <xs:documentation>
361 The condition that determines if the parent control will execute this action.
362 </xs:documentation>
363 </xs:annotation>
364 </xs:attribute>
365 <xs:attribute name="Page" type="xs:string" use="required">
366 <xs:annotation>
367 <xs:documentation>
368 The Name of the Page to show.
369 </xs:documentation>
370 </xs:annotation>
371 </xs:attribute>
372 </xs:complexType>
373 </xs:element>
374
375 <xs:element name="CloseWindowAction">
376 <xs:annotation>
377 <xs:documentation>
378 When the button is pressed, the WM_CLOSE message is sent to the window.
379 </xs:documentation>
380 </xs:annotation>
381 <xs:complexType>
382 <xs:attribute name="Condition" type="xs:string">
383 <xs:annotation>
384 <xs:documentation>
385 The condition that determines if the parent control will execute this action.
386 </xs:documentation>
387 </xs:annotation>
388 </xs:attribute>
389 </xs:complexType>
390 </xs:element>
391
392 <xs:element name="Checkbox">
393 <xs:annotation>
394 <xs:documentation>Defines a checkbox.</xs:documentation>
395 </xs:annotation>
396 <xs:complexType mixed="true">
397 <xs:annotation>
398 <xs:documentation>
399 Text to display beside the checkbox.
400 Mutually exclusive with the StringId attribute and child Text elements.
401 </xs:documentation>
402 </xs:annotation>
403 <xs:choice minOccurs="0" maxOccurs="unbounded">
404 <xs:element ref="Text" />
405 <xs:element ref="Tooltip" maxOccurs="1" />
406 </xs:choice>
407 <xs:attributeGroup ref="CommonControlAttributes" />
408 <xs:attribute name="FontId" type="xs:nonNegativeInteger" use="required">
409 <xs:annotation>
410 <xs:documentation>Numeric identifier to the Font element that serves as the font for the control.</xs:documentation>
411 </xs:annotation>
412 </xs:attribute>
413 <xs:attribute name="StringId" type="xs:nonNegativeInteger">
414 <xs:annotation>
415 <xs:documentation>
416 Identifier that references a string resource in the module to define the text for the control.
417 </xs:documentation>
418 </xs:annotation>
419 </xs:attribute>
420 </xs:complexType>
421 </xs:element>
422
423 <xs:element name="Combobox">
424 <xs:annotation>
425 <xs:documentation>Defines a combobox.</xs:documentation>
426 </xs:annotation>
427 <xs:complexType>
428 <xs:attributeGroup ref="CommonControlAttributes" />
429 <xs:attribute name="FontId" type="xs:nonNegativeInteger" use="required">
430 <xs:annotation>
431 <xs:documentation>Numeric identifier to the Font element that serves as the font for the control.</xs:documentation>
432 </xs:annotation>
433 </xs:attribute>
434 </xs:complexType>
435 </xs:element>
436
437 <xs:element name="CommandLink">
438 <xs:annotation>
439 <xs:documentation>Defines a button.</xs:documentation>
440 </xs:annotation>
441 <xs:complexType mixed="true">
442 <xs:annotation>
443 <xs:documentation>
444 Text to display in the button.
445 Mutually exclusive with the StringId attribute and child Text elements.
446 </xs:documentation>
447 </xs:annotation>
448 <xs:choice minOccurs="0" maxOccurs="unbounded">
449 <xs:annotation>
450 <xs:documentation>
451 If multiple Action elements are given, the conditions should be mutually exclusive (when multiple conditions are true, the behavior is undefined and could be changed at any time).
452 If none of the conditions of the Action elements are true, then it uses the Action element without the Condition attribute.
453 </xs:documentation>
454 </xs:annotation>
455 <xs:element ref="BrowseDirectoryAction" />
456 <xs:element ref="ChangePageAction" />
457 <xs:element ref="CloseWindowAction" />
458 <xs:element ref="Note" />
459 <xs:element ref="Text" />
460 </xs:choice>
461 <xs:attributeGroup ref="CommonControlAttributes" />
462 <xs:attribute name="FontId" type="xs:nonNegativeInteger" use="required">
463 <xs:annotation>
464 <xs:documentation>Numeric identifier to the Font element that serves as the font for the control. Only valid when using graphic buttons.</xs:documentation>
465 </xs:annotation>
466 </xs:attribute>
467 <xs:attribute name="IconFile" type="xs:string">
468 <xs:annotation>
469 <xs:documentation>
470 Relative path to an icon file to define a command link glyph.
471 Mutually exclusive with ImageResource and SourceX and SourceY attributes.
472 </xs:documentation>
473 </xs:annotation>
474 </xs:attribute>
475 <xs:attribute name="IconResource" type="xs:string">
476 <xs:annotation>
477 <xs:documentation>
478 Identifier that references an icon resource in the module to define a command link glyph.
479 Mutually exclusive with ImageFile and SourceX and SourceY attributes.
480 </xs:documentation>
481 </xs:annotation>
482 </xs:attribute>
483 <xs:attribute name="ImageFile" type="xs:string">
484 <xs:annotation>
485 <xs:documentation>
486 Relative path to an image file to define a command link glyph.
487 Mutually exclusive with ImageResource and SourceX and SourceY attributes.
488 </xs:documentation>
489 </xs:annotation>
490 </xs:attribute>
491 <xs:attribute name="ImageResource" type="xs:string">
492 <xs:annotation>
493 <xs:documentation>
494 Identifier that references an image resource in the module to define a command link glyph.
495 Mutually exclusive with ImageFile and SourceX and SourceY attributes.
496 </xs:documentation>
497 </xs:annotation>
498 </xs:attribute>
499 <xs:attribute name="StringId" type="xs:nonNegativeInteger">
500 <xs:annotation>
501 <xs:documentation>
502 Identifier that references a string resource in the module to define the text for the control.
503 </xs:documentation>
504 </xs:annotation>
505 </xs:attribute>
506 </xs:complexType>
507 </xs:element>
508
509 <xs:element name="Editbox">
510 <xs:annotation>
511 <xs:documentation>Defines an edit box.</xs:documentation>
512 </xs:annotation>
513 <xs:complexType>
514 <xs:simpleContent>
515 <xs:extension base="xs:string">
516 <xs:annotation>
517 <xs:documentation>
518 Initial text for the control.
519 Mutually exclusive with the StringId attribute.
520 </xs:documentation>
521 </xs:annotation>
522 <xs:attributeGroup ref="CommonControlAttributes" />
523 <xs:attribute name="FileSystemAutoComplete" type="YesNoType">
524 <xs:annotation>
525 <xs:documentation>Specifies whether the edit box should auto-complete with file system paths.</xs:documentation>
526 </xs:annotation>
527 </xs:attribute>
528 <xs:attribute name="FontId" type="xs:nonNegativeInteger" use="required">
529 <xs:annotation>
530 <xs:documentation>Numeric identifier to the Font element that serves as the font for the control.</xs:documentation>
531 </xs:annotation>
532 </xs:attribute>
533 <xs:attribute name="StringId" type="xs:nonNegativeInteger">
534 <xs:annotation>
535 <xs:documentation>
536 Identifier that references a string resource in the module to define the initial text for the control.
537 </xs:documentation>
538 </xs:annotation>
539 </xs:attribute>
540 </xs:extension>
541 </xs:simpleContent>
542 </xs:complexType>
543 </xs:element>
544
545 <xs:element name="Hyperlink">
546 <xs:annotation>
547 <xs:documentation>Defines a hyperlink.</xs:documentation>
548 </xs:annotation>
549 <xs:complexType mixed="true">
550 <xs:annotation>
551 <xs:documentation>
552 Text to display as the link.
553 Mutually exclusive with the StringId attribute and child Text elements.
554 </xs:documentation>
555 </xs:annotation>
556 <xs:choice minOccurs="0" maxOccurs="unbounded">
557 <xs:element ref="Text" />
558 <xs:element ref="Tooltip" maxOccurs="1" />
559 </xs:choice>
560 <xs:attributeGroup ref="CommonControlAttributes" />
561 <xs:attribute name="FontId" type="xs:nonNegativeInteger" use="required">
562 <xs:annotation>
563 <xs:documentation>Numeric identifier to the Font element that serves as the unselected font.</xs:documentation>
564 </xs:annotation>
565 </xs:attribute>
566 <xs:attribute name="HoverFontId" type="xs:nonNegativeInteger" use="required">
567 <xs:annotation>
568 <xs:documentation>Numeric identifier to the Font element that serves as the font when the control is hovered over.</xs:documentation>
569 </xs:annotation>
570 </xs:attribute>
571 <xs:attribute name="SelectedFontId" type="xs:nonNegativeInteger" use="required">
572 <xs:annotation>
573 <xs:documentation>Numeric identifier to the Font element that serves as the font when the control is selected.</xs:documentation>
574 </xs:annotation>
575 </xs:attribute>
576 <xs:attribute name="StringId" type="xs:nonNegativeInteger">
577 <xs:annotation>
578 <xs:documentation>
579 Identifier that references a string resource in the module to define the text for the control.
580 </xs:documentation>
581 </xs:annotation>
582 </xs:attribute>
583 </xs:complexType>
584 </xs:element>
585
586 <xs:element name="Hypertext">
587 <xs:annotation>
588 <xs:documentation>Defines a text block with support for HTML &lt;a&gt; tags.</xs:documentation>
589 </xs:annotation>
590 <xs:complexType mixed="true">
591 <xs:annotation>
592 <xs:documentation>
593 Text to display as the link.
594 Use HTML &lt;a href="URL"&gt; to create a link.
595 Mutually exclusive with the StringId attribute and child Text elements.
596 </xs:documentation>
597 </xs:annotation>
598 <xs:choice minOccurs="0" maxOccurs="unbounded">
599 <xs:element ref="Text" />
600 <xs:element ref="Tooltip" maxOccurs="1" />
601 </xs:choice>
602 <xs:attributeGroup ref="CommonControlAttributes" />
603 <xs:attribute name="FontId" type="xs:nonNegativeInteger" use="required">
604 <xs:annotation>
605 <xs:documentation>Numeric identifier to the Font element that serves as the font for the control.</xs:documentation>
606 </xs:annotation>
607 </xs:attribute>
608 <xs:attribute name="StringId" type="xs:nonNegativeInteger">
609 <xs:annotation>
610 <xs:documentation>
611 Identifier that references a string resource in the module to define the text for the control.
612 </xs:documentation>
613 </xs:annotation>
614 </xs:attribute>
615 </xs:complexType>
616 </xs:element>
617
618 <xs:element name="Image">
619 <xs:annotation>
620 <xs:documentation>Defines an image for an ImageList or Billboard.</xs:documentation>
621 </xs:annotation>
622 <xs:complexType>
623 <xs:attribute name="ImageFile" type="xs:string">
624 <xs:annotation>
625 <xs:documentation>Relative path to an image file. Mutually exclusive with ImageResource.</xs:documentation>
626 </xs:annotation>
627 </xs:attribute>
628 <xs:attribute name="ImageResource" type="xs:string">
629 <xs:annotation>
630 <xs:documentation>Identifier that references an image resource in the module. Mutually exclusive with ImageFile.</xs:documentation>
631 </xs:annotation>
632 </xs:attribute>
633 </xs:complexType>
634 </xs:element>
635
636 <xs:element name="ImageControl">
637 <xs:annotation>
638 <xs:documentation>Defines an image.</xs:documentation>
639 </xs:annotation>
640 <xs:complexType>
641 <xs:attributeGroup ref="CommonControlAttributes" />
642 <xs:attribute name="ImageFile" type="xs:string">
643 <xs:annotation>
644 <xs:documentation>Relative path to an image file. Mutually exclusive with ImageResource and SourceX and SourceY attributes.</xs:documentation>
645 </xs:annotation>
646 </xs:attribute>
647 <xs:attribute name="ImageResource" type="xs:string">
648 <xs:annotation>
649 <xs:documentation>Identifier that references an image resource in the module. Mutually exclusive with ImageFile and SourceX and SourceY attributes.</xs:documentation>
650 </xs:annotation>
651 </xs:attribute>
652 </xs:complexType>
653 </xs:element>
654
655 <xs:element name="Label">
656 <xs:annotation>
657 <xs:documentation>Defines a label.</xs:documentation>
658 </xs:annotation>
659 <xs:complexType mixed="true">
660 <xs:annotation>
661 <xs:documentation>
662 Text for the label to display.
663 Mutually exclusive with the StringId attribute and child Text elements.
664 </xs:documentation>
665 </xs:annotation>
666 <xs:choice minOccurs="0" maxOccurs="unbounded">
667 <xs:element ref="Text" />
668 <xs:element ref="Tooltip" maxOccurs="1" />
669 </xs:choice>
670 <xs:attributeGroup ref="CommonControlAttributes" />
671 <xs:attribute name="Center" type="YesNoType" use="optional">
672 <xs:annotation>
673 <xs:documentation>Specifies whether the text should be centered horizontally in the width of the control. Default is "no".</xs:documentation>
674 </xs:annotation>
675 </xs:attribute>
676 <xs:attribute name="DisablePrefix" type="YesNoType" use="optional">
677 <xs:annotation>
678 <xs:documentation>By default ampersands (&amp;) in the text will underline the next character and treat it as an accelerator key. Set this attribute to "yes" to disable that behavior. Default is "no".</xs:documentation>
679 </xs:annotation>
680 </xs:attribute>
681 <xs:attribute name="FontId" type="xs:nonNegativeInteger" use="required">
682 <xs:annotation>
683 <xs:documentation>Numeric identifier to the Font element that serves as the font for the control.</xs:documentation>
684 </xs:annotation>
685 </xs:attribute>
686 <xs:attribute name="StringId" type="xs:nonNegativeInteger">
687 <xs:annotation>
688 <xs:documentation>
689 Identifier that references a string resource in the module to define the text for the label.
690 </xs:documentation>
691 </xs:annotation>
692 </xs:attribute>
693 </xs:complexType>
694 </xs:element>
695
696 <xs:element name="ListView">
697 <xs:annotation>
698 <xs:documentation>Defines a listview.</xs:documentation>
699 </xs:annotation>
700 <xs:complexType>
701 <xs:choice maxOccurs="unbounded">
702 <xs:element ref="Column" />
703 </xs:choice>
704 <xs:attributeGroup ref="CommonControlAttributes" />
705 <xs:attribute name="FontId" type="xs:nonNegativeInteger">
706 <xs:annotation>
707 <xs:documentation>Numeric identifier to the Font element that serves as the default font for the ListView.</xs:documentation>
708 </xs:annotation>
709 </xs:attribute>
710 <xs:attribute name="HexExtendedStyle" type="xs:hexBinary">
711 <xs:annotation>
712 <xs:documentation>Hexadecimal extended window style.</xs:documentation>
713 </xs:annotation>
714 </xs:attribute>
715 <xs:attribute name="ImageList" type="xs:string">
716 <xs:annotation>
717 <xs:documentation>
718 The name of the ImageList to assign to this listview with type LVSIL_NORMAL.
719 </xs:documentation>
720 </xs:annotation>
721 </xs:attribute>
722 <xs:attribute name="ImageListSmall" type="xs:string">
723 <xs:annotation>
724 <xs:documentation>
725 The name of the ImageList to assign to this listview with type LVSIL_SMALL.
726 </xs:documentation>
727 </xs:annotation>
728 </xs:attribute>
729 <xs:attribute name="ImageListState" type="xs:string">
730 <xs:annotation>
731 <xs:documentation>
732 The name of the ImageList to assign to this listview with type LVSIL_STATE.
733 </xs:documentation>
734 </xs:annotation>
735 </xs:attribute>
736 <xs:attribute name="ImageListGroupHeader" type="xs:string">
737 <xs:annotation>
738 <xs:documentation>
739 The name of the ImageList to assign to this listview with type LVSIL_GROUPHEADER.
740 </xs:documentation>
741 </xs:annotation>
742 </xs:attribute>
743 </xs:complexType>
744 </xs:element>
745
746 <xs:element name="Note">
747 <xs:annotation>
748 <xs:documentation>
749 Defines note text for a command link control based on an optional condition.
750 If multiple Note elements are given for one control, the conditions should be mutually exclusive (when multiple conditions are true, the behavior is undefined and may be changed at any time).
751 If none of the conditions of a control's Note elements are true, then it uses the text of the Note element without the Condition attribute.
752 </xs:documentation>
753 </xs:annotation>
754 <xs:complexType>
755 <xs:simpleContent>
756 <xs:extension base="xs:string">
757 <xs:annotation>
758 <xs:documentation>
759 Note text for the parent command link control.
760 </xs:documentation>
761 </xs:annotation>
762 <xs:attribute name="Condition" type="xs:string">
763 <xs:annotation>
764 <xs:documentation>
765 The condition that determines when the parent control will use this note text.
766 </xs:documentation>
767 </xs:annotation>
768 </xs:attribute>
769 </xs:extension>
770 </xs:simpleContent>
771 </xs:complexType>
772 </xs:element>
773
774 <xs:element name="Panel">
775 <xs:annotation>
776 <xs:documentation>Defines a collection of controls.</xs:documentation>
777 </xs:annotation>
778 <xs:complexType>
779 <xs:group ref="ControlElements" maxOccurs="unbounded"/>
780 <xs:attributeGroup ref="CommonControlAttributes" />
781 </xs:complexType>
782 </xs:element>
783
784 <xs:element name="Progressbar">
785 <xs:annotation>
786 <xs:documentation>Defines a progress bar.</xs:documentation>
787 </xs:annotation>
788 <xs:complexType>
789 <xs:attributeGroup ref="CommonControlAttributes" />
790 <xs:attribute name="ImageFile" type="xs:string">
791 <xs:annotation>
792 <xs:documentation>Relative path to an image file for the control. The image must be 4 pixels wide: left pixel is the left side of progress bar, left middle pixel is progress used, right middle pixel is progress unused, right pixel is right side of progress bar. Mutually exclusive with ImageResource and SourceX and SourceY attributes.</xs:documentation>
793 </xs:annotation>
794 </xs:attribute>
795 <xs:attribute name="ImageResource" type="xs:string">
796 <xs:annotation>
797 <xs:documentation>Identifier that references an image resource in the module for the control. The image must be 4 pixels wide: left pixel is the left side of progress bar, left middle pixel is progress used, right middle pixel is progress unused, right pixel is right side of progress bar. Mutually exclusive with ImageFile and SourceX and SourceY attributes.</xs:documentation>
798 </xs:annotation>
799 </xs:attribute>
800 </xs:complexType>
801 </xs:element>
802
803 <xs:element name="RadioButton">
804 <xs:annotation>
805 <xs:documentation>Defines an individual radio button within a set of radio buttons.</xs:documentation>
806 </xs:annotation>
807 <xs:complexType mixed="true">
808 <xs:annotation>
809 <xs:documentation>
810 Text to display beside the radio button.
811 Mutually exclusive with the StringId attribute and child Text elements.
812 </xs:documentation>
813 </xs:annotation>
814 <xs:choice minOccurs="0" maxOccurs="unbounded">
815 <xs:element ref="Text" />
816 <xs:element ref="Tooltip" maxOccurs="1" />
817 </xs:choice>
818 <xs:attributeGroup ref="CommonControlAttributes" />
819 <xs:attribute name="FontId" type="xs:nonNegativeInteger" use="required">
820 <xs:annotation>
821 <xs:documentation>Numeric identifier to the Font element that serves as the font for the control.</xs:documentation>
822 </xs:annotation>
823 </xs:attribute>
824 <xs:attribute name="StringId" type="xs:nonNegativeInteger">
825 <xs:annotation>
826 <xs:documentation>
827 Identifier that references a string resource in the module to define the text for the control.
828 </xs:documentation>
829 </xs:annotation>
830 </xs:attribute>
831 <xs:attribute name="Value" type="xs:string">
832 <xs:annotation>
833 <xs:documentation>Optional value used when setting the variable associated with the set of radio buttons.</xs:documentation>
834 </xs:annotation>
835 </xs:attribute>
836 </xs:complexType>
837 </xs:element>
838
839 <xs:element name="RadioButtons">
840 <xs:annotation>
841 <xs:documentation>Defines a set of radio buttons.</xs:documentation>
842 </xs:annotation>
843 <xs:complexType>
844 <xs:choice maxOccurs="unbounded">
845 <xs:element ref="RadioButton" />
846 </xs:choice>
847 <xs:attribute name="Name" type="xs:string">
848 <xs:annotation>
849 <xs:documentation>Optional variable name for the set of radio buttons.</xs:documentation>
850 </xs:annotation>
851 </xs:attribute>
852 </xs:complexType>
853 </xs:element>
854
855 <xs:element name="Richedit">
856 <xs:annotation>
857 <xs:documentation>Defines a rich edit control.</xs:documentation>
858 </xs:annotation>
859 <xs:complexType mixed="true">
860 <xs:annotation>
861 <xs:documentation>
862 Initial text for the control.
863 Mutually exclusive with the StringId attribute.
864 </xs:documentation>
865 </xs:annotation>
866 <xs:choice minOccurs="0" maxOccurs="unbounded">
867 <xs:element ref="Text" />
868 <xs:element ref="Tooltip" maxOccurs="1" />
869 </xs:choice>
870 <xs:attributeGroup ref="CommonControlAttributes" />
871 <xs:attribute name="FontId" type="xs:nonNegativeInteger" use="required">
872 <xs:annotation>
873 <xs:documentation>
874 Numeric identifier to the Font element that serves as the font for the control.
875 </xs:documentation>
876 </xs:annotation>
877 </xs:attribute>
878 <xs:attribute name="StringId" type="xs:nonNegativeInteger">
879 <xs:annotation>
880 <xs:documentation>
881 Identifier that references a string resource in the module to define the initial text for the control.
882 </xs:documentation>
883 </xs:annotation>
884 </xs:attribute>
885 </xs:complexType>
886 </xs:element>
887
888 <xs:element name="Static">
889 <xs:annotation>
890 <xs:documentation>Defines a straight line.</xs:documentation>
891 </xs:annotation>
892 <xs:complexType>
893 <xs:attributeGroup ref="CommonControlAttributes" />
894 </xs:complexType>
895 </xs:element>
896
897 <xs:element name="Tab">
898 <xs:annotation>
899 <xs:documentation>Defines an individual tab within a set of tabs.</xs:documentation>
900 </xs:annotation>
901 <xs:complexType>
902 <xs:simpleContent>
903 <xs:extension base="xs:string">
904 <xs:annotation>
905 <xs:documentation>
906 Caption of the tab.
907 Mutually exclusive with the StringId attribute.
908 </xs:documentation>
909 </xs:annotation>
910 <xs:attribute name="StringId" type="xs:nonNegativeInteger">
911 <xs:annotation>
912 <xs:documentation>
913 Identifier that references a string resource in the module to define the caption of the tab.
914 </xs:documentation>
915 </xs:annotation>
916 </xs:attribute>
917 </xs:extension>
918 </xs:simpleContent>
919 </xs:complexType>
920 </xs:element>
921
922 <xs:element name="Tabs">
923 <xs:annotation>
924 <xs:documentation>Defines a set of tabs.</xs:documentation>
925 </xs:annotation>
926 <xs:complexType>
927 <xs:choice maxOccurs="unbounded">
928 <xs:element ref="Tab" />
929 </xs:choice>
930 <xs:attributeGroup ref="CommonControlAttributes" />
931 <xs:attribute name="FontId" type="xs:nonNegativeInteger" use="required">
932 <xs:annotation>
933 <xs:documentation>Numeric identifier to the Font element that serves as the font for the control.</xs:documentation>
934 </xs:annotation>
935 </xs:attribute>
936 </xs:complexType>
937 </xs:element>
938
939 <xs:element name="Text">
940 <xs:annotation>
941 <xs:documentation>
942 Defines text for the parent control based on an optional condition.
943 If multiple Text elements are given for one control, the conditions should be mutually exclusive (when multiple conditions are true, the behavior is undefined and may be changed at any time).
944 If none of the conditions of a control's Text elements are true, then it uses the text of the Text element without the Condition attribute.
945 </xs:documentation>
946 </xs:annotation>
947 <xs:complexType>
948 <xs:simpleContent>
949 <xs:extension base="xs:string">
950 <xs:annotation>
951 <xs:documentation>
952 Text for the parent control.
953 </xs:documentation>
954 </xs:annotation>
955 <xs:attribute name="Condition" type="xs:string">
956 <xs:annotation>
957 <xs:documentation>
958 The condition that determines when the parent control will use this text.
959 </xs:documentation>
960 </xs:annotation>
961 </xs:attribute>
962 </xs:extension>
963 </xs:simpleContent>
964 </xs:complexType>
965 </xs:element>
966
967 <xs:element name="Tooltip">
968 <xs:annotation>
969 <xs:documentation>
970 Defines text for the parent control's tooltip.
971 </xs:documentation>
972 </xs:annotation>
973 <xs:complexType>
974 <xs:simpleContent>
975 <xs:extension base="xs:string">
976 <xs:annotation>
977 <xs:documentation>
978 Text for the parent control's tooltip.
979 </xs:documentation>
980 </xs:annotation>
981 </xs:extension>
982 </xs:simpleContent>
983 </xs:complexType>
984 </xs:element>
985
986 <xs:element name="TreeView">
987 <xs:annotation>
988 <xs:documentation>Defines a treeview.</xs:documentation>
989 </xs:annotation>
990 <xs:complexType>
991 <xs:attributeGroup ref="CommonControlAttributes"/>
992 <xs:attribute name="AlwaysShowSelect">
993 <xs:annotation>
994 <xs:documentation>Specifies whether the row always appears selected even when the treeview has lost focus.</xs:documentation>
995 </xs:annotation>
996 </xs:attribute>
997 <xs:attribute name="EnableDragDrop">
998 <xs:annotation>
999 <xs:documentation>Specifies whether drag and drop is enabled for the treeview.</xs:documentation>
1000 </xs:annotation>
1001 </xs:attribute>
1002 <xs:attribute name="FullRowSelect">
1003 <xs:annotation>
1004 <xs:documentation>Specifies whether an entire row is selected for the treeview.</xs:documentation>
1005 </xs:annotation>
1006 </xs:attribute>
1007 <xs:attribute name="HasButtons">
1008 <xs:annotation>
1009 <xs:documentation>Specifies whether the treeview will show buttons.</xs:documentation>
1010 </xs:annotation>
1011 </xs:attribute>
1012 <xs:attribute name="HasLines">
1013 <xs:annotation>
1014 <xs:documentation>Specifies whether lines appear for all treeview items.</xs:documentation>
1015 </xs:annotation>
1016 </xs:attribute>
1017 <xs:attribute name="LinesAtRoot">
1018 <xs:annotation>
1019 <xs:documentation>Specifies whether the root nodes have lines beside them.</xs:documentation>
1020 </xs:annotation>
1021 </xs:attribute>
1022 </xs:complexType>
1023 </xs:element>
1024
1025 <xs:element name="Column">
1026 <xs:annotation>
1027 <xs:documentation>A column of a list.</xs:documentation>
1028 </xs:annotation>
1029 <xs:complexType>
1030 <xs:simpleContent>
1031 <xs:extension base="xs:string">
1032 <xs:annotation>
1033 <xs:documentation>
1034 Text for the column header.
1035 Mutually exclusive with the StringId attribute.
1036 </xs:documentation>
1037 </xs:annotation>
1038 <xs:attribute name="Width" type="xs:int">
1039 <xs:annotation>
1040 <xs:documentation>Width of the column.</xs:documentation>
1041 </xs:annotation>
1042 </xs:attribute>
1043 <xs:attribute name="Expands" type="YesNoType">
1044 <xs:annotation>
1045 <xs:documentation>
1046 Whether or not this column can grow to fill available width of the listview.
1047 More than one column can be marked with yes - all expandable columns will share available extra space.
1048 This is especially useful if the Window/@AutoResize is yes.
1049 </xs:documentation>
1050 </xs:annotation>
1051 </xs:attribute>
1052 <xs:attribute name="StringId" type="xs:nonNegativeInteger">
1053 <xs:annotation>
1054 <xs:documentation>
1055 Identifier that references a string resource in the module to define the text for the column header.
1056 </xs:documentation>
1057 </xs:annotation>
1058 </xs:attribute>
1059 </xs:extension>
1060 </xs:simpleContent>
1061 </xs:complexType>
1062 </xs:element>
1063
1064 <xs:group name="ControlElements">
1065 <xs:choice>
1066 <xs:element ref="Billboard" />
1067 <xs:element ref="Button" />
1068 <xs:element ref="Checkbox" />
1069 <xs:element ref="Combobox" />
1070 <xs:element ref="CommandLink" />
1071 <xs:element ref="Editbox" />
1072 <xs:element ref="Hyperlink" />
1073 <xs:element ref="Hypertext" />
1074 <xs:element ref="ImageControl" />
1075 <xs:element ref="Label" />
1076 <xs:element ref="ListView" />
1077 <xs:element ref="Panel" />
1078 <xs:element ref="Progressbar" />
1079 <xs:element ref="RadioButtons" />
1080 <xs:element ref="Richedit" />
1081 <xs:element ref="Static" />
1082 <xs:element ref="Tabs" />
1083 <xs:element ref="TreeView" />
1084 </xs:choice>
1085 </xs:group>
1086
1087 <xs:attributeGroup name="CommonControlAttributes">
1088 <xs:attribute name="Name" type="xs:string">
1089 <xs:annotation>
1090 <xs:documentation>Optional name for the control.</xs:documentation>
1091 </xs:annotation>
1092 </xs:attribute>
1093 <xs:attribute name="DisableAutomaticBehavior" type="YesNoType">
1094 <xs:annotation>
1095 <xs:documentation>Set to 'yes' to disable automatic variable getting and setting, EnableCondition, VisibleCondition, and conditional Text elements. The default is 'no'.</xs:documentation>
1096 </xs:annotation>
1097 </xs:attribute>
1098 <xs:attribute name="EnableCondition" type="xs:string">
1099 <xs:annotation>
1100 <xs:documentation>A condition that determines if the control is enabled. If this condition is true or omitted, then the control will be enabled.</xs:documentation>
1101 </xs:annotation>
1102 </xs:attribute>
1103 <xs:attribute name="Height" type="xs:int" use="required">
1104 <xs:annotation>
1105 <xs:documentation>Height of the control. Non-positive values extend the control to the bottom of the window minus the value.</xs:documentation>
1106 </xs:annotation>
1107 </xs:attribute>
1108 <xs:attribute name="HexStyle" type="xs:hexBinary">
1109 <xs:annotation>
1110 <xs:documentation>Hexadecimal window style for the control.</xs:documentation>
1111 </xs:annotation>
1112 </xs:attribute>
1113 <xs:attribute name="HideWhenDisabled" type="YesNoType">
1114 <xs:annotation>
1115 <xs:documentation>Specifies whether the control should be hidden when disabled.</xs:documentation>
1116 </xs:annotation>
1117 </xs:attribute>
1118 <xs:attribute name="TabStop" type="YesNoType">
1119 <xs:annotation>
1120 <xs:documentation>Specifies whether the control is part of the tab sequence of controls.</xs:documentation>
1121 </xs:annotation>
1122 </xs:attribute>
1123 <xs:attribute name="Visible" type="YesNoType">
1124 <xs:annotation>
1125 <xs:documentation>Specifies whether the control is initially visible.</xs:documentation>
1126 </xs:annotation>
1127 </xs:attribute>
1128 <xs:attribute name="VisibleCondition" type="xs:string">
1129 <xs:annotation>
1130 <xs:documentation>
1131 A condition that determines if the control is visible. If this condition is true or omitted, then the control will be visible.
1132 </xs:documentation>
1133 </xs:annotation>
1134 </xs:attribute>
1135 <xs:attribute name="Width" type="xs:int" use="required">
1136 <xs:annotation>
1137 <xs:documentation>Width of the control. Non-positive values extend the control to the right of the window minus the value.</xs:documentation>
1138 </xs:annotation>
1139 </xs:attribute>
1140 <xs:attribute name="X" type="xs:int" use="required">
1141 <xs:annotation>
1142 <xs:documentation>X coordinate for the control from the left of the window. Negative values are coordinates from the right of the window minus the width of the control.</xs:documentation>
1143 </xs:annotation>
1144 </xs:attribute>
1145 <xs:attribute name="Y" type="xs:int" use="required">
1146 <xs:annotation>
1147 <xs:documentation>Y coordinate for the control from the top of the window. Negative values are coordinates from the bottom of the window minus the height of the control.</xs:documentation>
1148 </xs:annotation>
1149 </xs:attribute>
1150 </xs:attributeGroup>
1151
1152 <xs:simpleType name="YesNoType">
1153 <xs:annotation>
1154 <xs:documentation>Values of this type will either be "yes" or "no".</xs:documentation>
1155 </xs:annotation>
1156 <xs:restriction base="xs:NMTOKEN">
1157 <xs:enumeration value="no"/>
1158 <xs:enumeration value="yes"/>
1159 </xs:restriction>
1160 </xs:simpleType>
1161
1162 <xs:simpleType name="SystemColorType">
1163 <xs:annotation>
1164 <xs:documentation>
1165 Indicates a system color for a font.
1166 </xs:documentation>
1167 </xs:annotation>
1168 <xs:restriction base="xs:NMTOKEN">
1169 <xs:enumeration value="btnface" />
1170 <xs:enumeration value="btntext" />
1171 <xs:enumeration value="graytext" />
1172 <xs:enumeration value="highlight" />
1173 <xs:enumeration value="highlighttext" />
1174 <xs:enumeration value="hotlight" />
1175 <xs:enumeration value="window" />
1176 <xs:enumeration value="windowtext" />
1177 </xs:restriction>
1178 </xs:simpleType>
1179
1180 <xs:simpleType name="FontColorType">
1181 <xs:annotation>
1182 <xs:documentation>
1183 Indicates the foreground or background color of a font.
1184 </xs:documentation>
1185 </xs:annotation>
1186 <xs:union memberTypes="SystemColorType xs:string"/>
1187 </xs:simpleType>
1188</xs:schema>
diff --git a/src/libs/dutil/appveyor.cmd b/src/libs/dutil/appveyor.cmd
new file mode 100644
index 00000000..85476b8e
--- /dev/null
+++ b/src/libs/dutil/appveyor.cmd
@@ -0,0 +1,24 @@
1@setlocal
2@pushd %~dp0
3@set _C=Release
4@if /i "%1"=="debug" set _C=Debug
5
6nuget restore || exit /b
7
8msbuild -t:Test -p:Configuration=%_C% src\test\DUtilUnitTest || exit /b
9
10msbuild -p:Configuration=%_C%;Platform=x86;PlatformToolset=v140 || exit /b
11msbuild -p:Configuration=%_C%;Platform=x64;PlatformToolset=v140 || exit /b
12
13msbuild -p:Configuration=%_C%;Platform=x86;PlatformToolset=v141 || exit /b
14msbuild -p:Configuration=%_C%;Platform=x64;PlatformToolset=v141 || exit /b
15msbuild -p:Configuration=%_C%;Platform=ARM64;PlatformToolset=v141 || exit /b
16
17msbuild -p:Configuration=%_C%;Platform=x86;PlatformToolset=v142 || exit /b
18msbuild -p:Configuration=%_C%;Platform=x64;PlatformToolset=v142 || exit /b
19msbuild -p:Configuration=%_C%;Platform=ARM64;PlatformToolset=v142 || exit /b
20
21msbuild -p:Configuration=%_C% -t:PackNative src\dutil\dutil.vcxproj || exit /b
22
23@popd
24@endlocal \ No newline at end of file
diff --git a/src/libs/dutil/appveyor.yml b/src/libs/dutil/appveyor.yml
new file mode 100644
index 00000000..f602d07c
--- /dev/null
+++ b/src/libs/dutil/appveyor.yml
@@ -0,0 +1,44 @@
1# Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2#
3# Do NOT modify this file. Update the canonical version in Home\repo-template\src\appveyor.yml
4# then update all of the repos.
5
6branches:
7 only:
8 - master
9 - develop
10
11image: Visual Studio 2019
12
13version: 0.0.0.{build}
14configuration: Release
15
16environment:
17 DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
18 DOTNET_CLI_TELEMETRY_OPTOUT: 1
19 NUGET_XMLDOC_MODE: skip
20
21build_script:
22 - appveyor.cmd
23
24test: off
25
26pull_requests:
27 do_not_increment_build_number: true
28
29nuget:
30 disable_publish_on_pr: true
31
32skip_branch_with_pr: true
33skip_tags: true
34
35artifacts:
36- path: build\Release\**\*.nupkg
37 name: nuget
38- path: build\Release\**\*.msi
39 name: msi
40
41notifications:
42- provider: Slack
43 incoming_webhook:
44 secure: p5xuu+4x2JHfwGDMDe5KcG1k7gZxqYc4jWVwvyNZv5cvkubPD2waJs5yXMAXZNN7Z63/3PWHb7q4KoY/99AjauYa1nZ4c5qYqRPFRBKTHfA=
diff --git a/src/libs/dutil/dutil.sln b/src/libs/dutil/dutil.sln
new file mode 100644
index 00000000..433f42a5
--- /dev/null
+++ b/src/libs/dutil/dutil.sln
@@ -0,0 +1,47 @@
1
2Microsoft Visual Studio Solution File, Format Version 12.00
3# Visual Studio Version 16
4VisualStudioVersion = 16.0.30711.63
5MinimumVisualStudioVersion = 15.0.26124.0
6Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dutil", "src\dutil\dutil.vcxproj", "{1244E671-F108-4334-BA52-8A7517F26ECD}"
7EndProject
8Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DUtilUnitTest", "src\test\DUtilUnitTest\DUtilUnitTest.vcxproj", "{AB7EE608-E5FB-42A5-831F-0DEEEA141223}"
9EndProject
10Global
11 GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 Debug|ARM64 = Debug|ARM64
13 Debug|x64 = Debug|x64
14 Debug|x86 = Debug|x86
15 Release|ARM64 = Release|ARM64
16 Release|x64 = Release|x64
17 Release|x86 = Release|x86
18 EndGlobalSection
19 GlobalSection(ProjectConfigurationPlatforms) = postSolution
20 {1244E671-F108-4334-BA52-8A7517F26ECD}.Debug|ARM64.ActiveCfg = Debug|ARM64
21 {1244E671-F108-4334-BA52-8A7517F26ECD}.Debug|ARM64.Build.0 = Debug|ARM64
22 {1244E671-F108-4334-BA52-8A7517F26ECD}.Debug|x64.ActiveCfg = Debug|x64
23 {1244E671-F108-4334-BA52-8A7517F26ECD}.Debug|x64.Build.0 = Debug|x64
24 {1244E671-F108-4334-BA52-8A7517F26ECD}.Debug|x86.ActiveCfg = Debug|Win32
25 {1244E671-F108-4334-BA52-8A7517F26ECD}.Debug|x86.Build.0 = Debug|Win32
26 {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|ARM64.ActiveCfg = Release|ARM64
27 {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|ARM64.Build.0 = Release|ARM64
28 {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|x64.ActiveCfg = Release|x64
29 {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|x64.Build.0 = Release|x64
30 {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|x86.ActiveCfg = Release|Win32
31 {1244E671-F108-4334-BA52-8A7517F26ECD}.Release|x86.Build.0 = Release|Win32
32 {AB7EE608-E5FB-42A5-831F-0DEEEA141223}.Debug|ARM64.ActiveCfg = Debug|Win32
33 {AB7EE608-E5FB-42A5-831F-0DEEEA141223}.Debug|x64.ActiveCfg = Debug|Win32
34 {AB7EE608-E5FB-42A5-831F-0DEEEA141223}.Debug|x86.ActiveCfg = Debug|Win32
35 {AB7EE608-E5FB-42A5-831F-0DEEEA141223}.Debug|x86.Build.0 = Debug|Win32
36 {AB7EE608-E5FB-42A5-831F-0DEEEA141223}.Release|ARM64.ActiveCfg = Release|Win32
37 {AB7EE608-E5FB-42A5-831F-0DEEEA141223}.Release|x64.ActiveCfg = Release|Win32
38 {AB7EE608-E5FB-42A5-831F-0DEEEA141223}.Release|x86.ActiveCfg = Release|Win32
39 {AB7EE608-E5FB-42A5-831F-0DEEEA141223}.Release|x86.Build.0 = Release|Win32
40 EndGlobalSection
41 GlobalSection(SolutionProperties) = preSolution
42 HideSolutionNode = FALSE
43 EndGlobalSection
44 GlobalSection(ExtensibilityGlobals) = postSolution
45 SolutionGuid = {DD209744-C40E-4C34-8CB4-BC6B71F9A133}
46 EndGlobalSection
47EndGlobal
diff --git a/src/libs/dutil/nuget.config b/src/libs/dutil/nuget.config
new file mode 100644
index 00000000..d5ef8952
--- /dev/null
+++ b/src/libs/dutil/nuget.config
@@ -0,0 +1,8 @@
1<?xml version="1.0" encoding="utf-8"?>
2<configuration>
3 <packageSources>
4 <clear />
5 <add key="wixbuildtools" value="https://ci.appveyor.com/nuget/wixbuildtools" />
6 <add key="api.nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
7 </packageSources>
8</configuration> \ No newline at end of file
diff --git a/src/libs/dutil/test/DUtilUnitTest/ApupUtilTests.cpp b/src/libs/dutil/test/DUtilUnitTest/ApupUtilTests.cpp
new file mode 100644
index 00000000..30a45f5a
--- /dev/null
+++ b/src/libs/dutil/test/DUtilUnitTest/ApupUtilTests.cpp
@@ -0,0 +1,46 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5using namespace System;
6using namespace Xunit;
7using namespace WixBuildTools::TestSupport;
8
9namespace DutilTests
10{
11 public ref class ApupUtil
12 {
13 public:
14 [Fact]
15 void AllocChainFromAtomSortsDescending()
16 {
17 HRESULT hr = S_OK;
18 ATOM_FEED* pFeed = NULL;
19 APPLICATION_UPDATE_CHAIN* pChain = NULL;
20
21 DutilInitialize(&DutilTestTraceError);
22
23 try
24 {
25 XmlInitialize();
26 NativeAssert::Succeeded(hr, "Failed to initialize Xml.");
27
28 pin_ptr<const wchar_t> feedFilePath = PtrToStringChars(TestData::Get("TestData", "ApupUtilTests", "FeedBv2.0.xml"));
29 hr = AtomParseFromFile(feedFilePath, &pFeed);
30 NativeAssert::Succeeded(hr, "Failed to parse feed: {0}", feedFilePath);
31
32 hr = ApupAllocChainFromAtom(pFeed, &pChain);
33 NativeAssert::Succeeded(hr, "Failed to get chain from feed.");
34
35 Assert::Equal(3ul, pChain->cEntries);
36 NativeAssert::StringEqual(L"Bundle v2.0", pChain->rgEntries[0].wzTitle);
37 NativeAssert::StringEqual(L"Bundle v1.0", pChain->rgEntries[1].wzTitle);
38 NativeAssert::StringEqual(L"Bundle v1.0-preview", pChain->rgEntries[2].wzTitle);
39 }
40 finally
41 {
42 DutilUninitialize();
43 }
44 }
45 };
46}
diff --git a/src/libs/dutil/test/DUtilUnitTest/AssemblyInfo.cpp b/src/libs/dutil/test/DUtilUnitTest/AssemblyInfo.cpp
new file mode 100644
index 00000000..2d527910
--- /dev/null
+++ b/src/libs/dutil/test/DUtilUnitTest/AssemblyInfo.cpp
@@ -0,0 +1,12 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5using namespace System::Reflection;
6using namespace System::Runtime::CompilerServices;
7using namespace System::Runtime::InteropServices;
8
9[assembly: AssemblyTitleAttribute("Windows Installer XML Dutil unit tests")];
10[assembly: AssemblyDescriptionAttribute("Dutil unit tests")];
11[assembly: AssemblyCultureAttribute("")];
12[assembly: ComVisible(false)];
diff --git a/src/libs/dutil/test/DUtilUnitTest/DUtilTests.cpp b/src/libs/dutil/test/DUtilUnitTest/DUtilTests.cpp
new file mode 100644
index 00000000..55e81d46
--- /dev/null
+++ b/src/libs/dutil/test/DUtilUnitTest/DUtilTests.cpp
@@ -0,0 +1,35 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5using namespace System;
6using namespace Xunit;
7using namespace WixBuildTools::TestSupport;
8
9namespace DutilTests
10{
11 public ref class DUtil
12 {
13 public:
14 [Fact]
15 void DUtilTraceErrorSourceFiltersOnTraceLevel()
16 {
17 DutilInitialize(&DutilTestTraceError);
18
19 CallDutilTraceErrorSource();
20
21 Dutil_TraceSetLevel(REPORT_DEBUG, FALSE);
22
23 Action^ action = gcnew Action(this, &DUtil::CallDutilTraceErrorSource);
24 Assert::Throws<Exception^>(action);
25
26 DutilUninitialize();
27 }
28
29 private:
30 void CallDutilTraceErrorSource()
31 {
32 Dutil_TraceErrorSource(__FILE__, __LINE__, REPORT_DEBUG, DUTIL_SOURCE_EXTERNAL, E_FAIL, "Error message");
33 }
34 };
35}
diff --git a/src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj b/src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj
new file mode 100644
index 00000000..18410e5d
--- /dev/null
+++ b/src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj
@@ -0,0 +1,99 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3
4<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
5 <Import Project="..\..\..\packages\WixBuildTools.TestSupport.Native.4.0.47\build\WixBuildTools.TestSupport.Native.props" Condition="Exists('..\..\..\packages\WixBuildTools.TestSupport.Native.4.0.47\build\WixBuildTools.TestSupport.Native.props')" />
6 <ItemGroup Label="ProjectConfigurations">
7 <ProjectConfiguration Include="Debug|Win32">
8 <Configuration>Debug</Configuration>
9 <Platform>Win32</Platform>
10 </ProjectConfiguration>
11 <ProjectConfiguration Include="Release|Win32">
12 <Configuration>Release</Configuration>
13 <Platform>Win32</Platform>
14 </ProjectConfiguration>
15 </ItemGroup>
16
17 <PropertyGroup Label="Globals">
18 <ProjectTypes>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}</ProjectTypes>
19 <ProjectGuid>{AB7EE608-E5FB-42A5-831F-0DEEEA141223}</ProjectGuid>
20 <RootNamespace>DUtilUnitTests</RootNamespace>
21 <Keyword>ManagedCProj</Keyword>
22 <ConfigurationType>DynamicLibrary</ConfigurationType>
23 <CharacterSet>Unicode</CharacterSet>
24 <CLRSupport>true</CLRSupport>
25 <SignOutput>false</SignOutput>
26 </PropertyGroup>
27
28 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
29 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
30
31 <PropertyGroup>
32 <ProjectAdditionalIncludeDirectories>..\..\dutil\inc</ProjectAdditionalIncludeDirectories>
33 <ProjectAdditionalLinkLibraries>rpcrt4.lib;Mpr.lib;Ws2_32.lib;urlmon.lib;wininet.lib</ProjectAdditionalLinkLibraries>
34 </PropertyGroup>
35
36 <ItemGroup>
37 <ClCompile Include="ApupUtilTests.cpp" />
38 <ClCompile Include="AssemblyInfo.cpp" />
39 <ClCompile Include="DictUtilTest.cpp" />
40 <ClCompile Include="DirUtilTests.cpp" />
41 <ClCompile Include="DUtilTests.cpp" />
42 <ClCompile Include="error.cpp" />
43 <ClCompile Include="FileUtilTest.cpp" />
44 <ClCompile Include="GuidUtilTest.cpp" />
45 <ClCompile Include="IniUtilTest.cpp" />
46 <ClCompile Include="MemUtilTest.cpp" />
47 <ClCompile Include="MonUtilTest.cpp" />
48 <ClCompile Include="PathUtilTest.cpp" />
49 <ClCompile Include="precomp.cpp">
50 <PrecompiledHeader>Create</PrecompiledHeader>
51 <!-- Warnings from referencing netstandard dlls -->
52 <DisableSpecificWarnings>4564;4691</DisableSpecificWarnings>
53 </ClCompile>
54 <ClCompile Include="SceUtilTest.cpp" Condition=" Exists('$(SqlCESdkIncludePath)') " />
55 <ClCompile Include="StrUtilTest.cpp" />
56 <ClCompile Include="UriUtilTest.cpp" />
57 <ClCompile Include="VerUtilTests.cpp" />
58 </ItemGroup>
59
60 <ItemGroup>
61 <ClInclude Include="precomp.h" />
62 <ClInclude Include="error.h" />
63 </ItemGroup>
64
65 <ItemGroup>
66 <None Include="packages.config" />
67 <ResourceCompile Include="UnitTest.rc" />
68 </ItemGroup>
69
70 <ItemGroup>
71 <None Include="TestData\ApupUtilTests\FeedBv2.0.xml" CopyToOutputDirectory="PreserveNewest" />
72 </ItemGroup>
73
74 <ItemGroup>
75 <Reference Include="System" />
76 <Reference Include="System.Core" />
77 <Reference Include="WixBuildTools.TestSupport">
78 <HintPath>..\..\..\packages\WixBuildTools.TestSupport.4.0.47\lib\net472\WixBuildTools.TestSupport.dll</HintPath>
79 </Reference>
80 <Reference Include="WixBuildTools.TestSupport.Native">
81 <HintPath>..\..\..\packages\WixBuildTools.TestSupport.Native.4.0.47\lib\net472\WixBuildTools.TestSupport.Native.dll</HintPath>
82 </Reference>
83 </ItemGroup>
84
85 <ItemGroup>
86 <ProjectReference Include="..\..\dutil\dutil.vcxproj">
87 <Project>{1244E671-F108-4334-BA52-8A7517F26ECD}</Project>
88 </ProjectReference>
89 </ItemGroup>
90 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
91 <Import Project="..\..\..\packages\WixBuildTools.TestSupport.Native.4.0.47\build\WixBuildTools.TestSupport.Native.targets" Condition="Exists('..\..\..\packages\WixBuildTools.TestSupport.Native.4.0.47\build\WixBuildTools.TestSupport.Native.targets')" />
92 <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
93 <PropertyGroup>
94 <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>
95 </PropertyGroup>
96 <Error Condition="!Exists('..\..\..\packages\WixBuildTools.TestSupport.Native.4.0.47\build\WixBuildTools.TestSupport.Native.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\WixBuildTools.TestSupport.Native.4.0.47\build\WixBuildTools.TestSupport.Native.props'))" />
97 <Error Condition="!Exists('..\..\..\packages\WixBuildTools.TestSupport.Native.4.0.47\build\WixBuildTools.TestSupport.Native.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\WixBuildTools.TestSupport.Native.4.0.47\build\WixBuildTools.TestSupport.Native.targets'))" />
98 </Target>
99</Project> \ No newline at end of file
diff --git a/src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters b/src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters
new file mode 100644
index 00000000..4df7af89
--- /dev/null
+++ b/src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters
@@ -0,0 +1,80 @@
1<?xml version="1.0" encoding="utf-8"?>
2<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3 <ItemGroup>
4 <Filter Include="Source Files">
5 <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
6 <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
7 </Filter>
8 <Filter Include="Header Files">
9 <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
10 <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
11 </Filter>
12 <Filter Include="Resource Files">
13 <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
14 <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
15 </Filter>
16 </ItemGroup>
17 <ItemGroup>
18 <ClCompile Include="ApupUtilTests.cpp">
19 <Filter>Source Files</Filter>
20 </ClCompile>
21 <ClCompile Include="AssemblyInfo.cpp">
22 <Filter>Source Files</Filter>
23 </ClCompile>
24 <ClCompile Include="DictUtilTest.cpp">
25 <Filter>Source Files</Filter>
26 </ClCompile>
27 <ClCompile Include="DirUtilTests.cpp">
28 <Filter>Source Files</Filter>
29 </ClCompile>
30 <ClCompile Include="DUtilTests.cpp">
31 <Filter>Source Files</Filter>
32 </ClCompile>
33 <ClCompile Include="error.cpp">
34 <Filter>Source Files</Filter>
35 </ClCompile>
36 <ClCompile Include="FileUtilTest.cpp">
37 <Filter>Source Files</Filter>
38 </ClCompile>
39 <ClCompile Include="GuidUtilTest.cpp">
40 <Filter>Source Files</Filter>
41 </ClCompile>
42 <ClCompile Include="IniUtilTest.cpp">
43 <Filter>Source Files</Filter>
44 </ClCompile>
45 <ClCompile Include="MemUtilTest.cpp">
46 <Filter>Source Files</Filter>
47 </ClCompile>
48 <ClCompile Include="MonUtilTest.cpp">
49 <Filter>Source Files</Filter>
50 </ClCompile>
51 <ClCompile Include="PathUtilTest.cpp">
52 <Filter>Source Files</Filter>
53 </ClCompile>
54 <ClCompile Include="precomp.cpp">
55 <Filter>Source Files</Filter>
56 </ClCompile>
57 <ClCompile Include="StrUtilTest.cpp">
58 <Filter>Source Files</Filter>
59 </ClCompile>
60 <ClCompile Include="UriUtilTest.cpp">
61 <Filter>Source Files</Filter>
62 </ClCompile>
63 <ClCompile Include="VerUtilTests.cpp">
64 <Filter>Source Files</Filter>
65 </ClCompile>
66 </ItemGroup>
67 <ItemGroup>
68 <ResourceCompile Include="UnitTest.rc">
69 <Filter>Resource Files</Filter>
70 </ResourceCompile>
71 </ItemGroup>
72 <ItemGroup>
73 <ClInclude Include="precomp.h">
74 <Filter>Header Files</Filter>
75 </ClInclude>
76 <ClInclude Include="error.h">
77 <Filter>Header Files</Filter>
78 </ClInclude>
79 </ItemGroup>
80</Project> \ No newline at end of file
diff --git a/src/libs/dutil/test/DUtilUnitTest/DictUtilTest.cpp b/src/libs/dutil/test/DUtilUnitTest/DictUtilTest.cpp
new file mode 100644
index 00000000..4b4777d7
--- /dev/null
+++ b/src/libs/dutil/test/DUtilUnitTest/DictUtilTest.cpp
@@ -0,0 +1,191 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5using namespace System;
6using namespace Xunit;
7using namespace WixBuildTools::TestSupport;
8
9const DWORD numIterations = 100000;
10
11namespace DutilTests
12{
13 struct Value
14 {
15 DWORD dwNum;
16 LPWSTR sczKey;
17 };
18
19 public ref class DictUtil
20 {
21 public:
22 [Fact]
23 void DictUtilTest()
24 {
25 DutilInitialize(&DutilTestTraceError);
26
27 EmbeddedKeyTestHelper(DICT_FLAG_NONE, numIterations);
28
29 EmbeddedKeyTestHelper(DICT_FLAG_CASEINSENSITIVE, numIterations);
30
31 StringListTestHelper(DICT_FLAG_NONE, numIterations);
32
33 StringListTestHelper(DICT_FLAG_CASEINSENSITIVE, numIterations);
34
35 DutilUninitialize();
36 }
37
38 private:
39 void EmbeddedKeyTestHelper(DICT_FLAG dfFlags, DWORD dwNumIterations)
40 {
41 HRESULT hr = S_OK;
42 Value *rgValues = NULL;
43 Value *valueFound = NULL;
44 DWORD cValues = 0;
45 LPWSTR sczExpectedKey = NULL;
46 STRINGDICT_HANDLE sdValues = NULL;
47
48 try
49 {
50 hr = DictCreateWithEmbeddedKey(&sdValues, 0, (void **)&rgValues, offsetof(Value, sczKey), dfFlags);
51 NativeAssert::Succeeded(hr, "Failed to create dictionary of values");
52
53 for (DWORD i = 0; i < dwNumIterations; ++i)
54 {
55 cValues++;
56
57 hr = MemEnsureArraySize((void **)&rgValues, cValues, sizeof(Value), 5);
58 NativeAssert::Succeeded(hr, "Failed to grow value array");
59
60 hr = StrAllocFormatted(&rgValues[i].sczKey, L"%u_a_%u", i, i);
61 NativeAssert::Succeeded(hr, "Failed to allocate key for value {0}", i);
62
63 hr = DictAddValue(sdValues, rgValues + i);
64 NativeAssert::Succeeded(hr, "Failed to add item {0} to dict", i);
65 }
66
67 for (DWORD i = 0; i < dwNumIterations; ++i)
68 {
69 hr = StrAllocFormatted(&sczExpectedKey, L"%u_a_%u", i, i);
70 NativeAssert::Succeeded(hr, "Failed to allocate expected key {0}", i);
71
72 hr = DictGetValue(sdValues, sczExpectedKey, (void **)&valueFound);
73 NativeAssert::Succeeded(hr, "Failed to find value {0}", sczExpectedKey);
74
75 NativeAssert::StringEqual(sczExpectedKey, valueFound->sczKey);
76
77 hr = StrAllocFormatted(&sczExpectedKey, L"%u_A_%u", i, i);
78 NativeAssert::Succeeded(hr, "Failed to allocate uppercase expected key {0}", i);
79
80 hr = DictGetValue(sdValues, sczExpectedKey, (void **)&valueFound);
81
82 if (dfFlags & DICT_FLAG_CASEINSENSITIVE)
83 {
84 NativeAssert::Succeeded(hr, "Failed to find value {0}", sczExpectedKey);
85
86 NativeAssert::StringEqual(sczExpectedKey, valueFound->sczKey, TRUE);
87 }
88 else
89 {
90 if (E_NOTFOUND != hr)
91 {
92 hr = E_FAIL;
93 ExitOnFailure(hr, "This embedded key is case sensitive, but it seemed to have found something case using case insensitivity!: %ls", sczExpectedKey);
94 }
95 }
96
97 hr = StrAllocFormatted(&sczExpectedKey, L"%u_b_%u", i, i);
98 NativeAssert::Succeeded(hr, "Failed to allocate unexpected key {0}", i);
99
100 hr = DictGetValue(sdValues, sczExpectedKey, (void **)&valueFound);
101 if (E_NOTFOUND != hr)
102 {
103 hr = E_FAIL;
104 ExitOnFailure(hr, "Item shouldn't have been found in dictionary: %ls", sczExpectedKey);
105 }
106 }
107 }
108 finally
109 {
110 for (DWORD i = 0; i < cValues; ++i)
111 {
112 ReleaseStr(rgValues[i].sczKey);
113 }
114 ReleaseMem(rgValues);
115 ReleaseStr(sczExpectedKey);
116 ReleaseDict(sdValues);
117 }
118
119 LExit:
120 return;
121 }
122
123 void StringListTestHelper(DICT_FLAG dfFlags, DWORD dwNumIterations)
124 {
125 HRESULT hr = S_OK;
126 LPWSTR sczKey = NULL;
127 LPWSTR sczExpectedKey = NULL;
128 STRINGDICT_HANDLE sdValues = NULL;
129
130 try
131 {
132 hr = DictCreateStringList(&sdValues, 0, dfFlags);
133 NativeAssert::Succeeded(hr, "Failed to create dictionary of keys");
134
135 for (DWORD i = 0; i < dwNumIterations; ++i)
136 {
137 hr = StrAllocFormatted(&sczKey, L"%u_a_%u", i, i);
138 NativeAssert::Succeeded(hr, "Failed to allocate key for value {0}", i);
139
140 hr = DictAddKey(sdValues, sczKey);
141 NativeAssert::Succeeded(hr, "Failed to add key {0} to dict", i);
142 }
143
144 for (DWORD i = 0; i < dwNumIterations; ++i)
145 {
146 hr = StrAllocFormatted(&sczExpectedKey, L"%u_a_%u", i, i);
147 NativeAssert::Succeeded(hr, "Failed to allocate expected key {0}", i);
148
149 hr = DictKeyExists(sdValues, sczExpectedKey);
150 NativeAssert::Succeeded(hr, "Failed to find value {0}", sczExpectedKey);
151
152 hr = StrAllocFormatted(&sczExpectedKey, L"%u_A_%u", i, i);
153 NativeAssert::Succeeded(hr, "Failed to allocate uppercase expected key {0}", i);
154
155 hr = DictKeyExists(sdValues, sczExpectedKey);
156 if (dfFlags & DICT_FLAG_CASEINSENSITIVE)
157 {
158 NativeAssert::Succeeded(hr, "Failed to find value {0}", sczExpectedKey);
159 }
160 else
161 {
162 if (E_NOTFOUND != hr)
163 {
164 hr = E_FAIL;
165 ExitOnFailure(hr, "This stringlist dict is case sensitive, but it seemed to have found something case using case insensitivity!: %ls", sczExpectedKey);
166 }
167 }
168
169 hr = StrAllocFormatted(&sczExpectedKey, L"%u_b_%u", i, i);
170 NativeAssert::Succeeded(hr, "Failed to allocate unexpected key {0}", i);
171
172 hr = DictKeyExists(sdValues, sczExpectedKey);
173 if (E_NOTFOUND != hr)
174 {
175 hr = E_FAIL;
176 ExitOnFailure(hr, "Item shouldn't have been found in dictionary: %ls", sczExpectedKey);
177 }
178 }
179 }
180 finally
181 {
182 ReleaseStr(sczKey);
183 ReleaseStr(sczExpectedKey);
184 ReleaseDict(sdValues);
185 }
186
187 LExit:
188 return;
189 }
190 };
191}
diff --git a/src/libs/dutil/test/DUtilUnitTest/DirUtilTests.cpp b/src/libs/dutil/test/DUtilUnitTest/DirUtilTests.cpp
new file mode 100644
index 00000000..7643366f
--- /dev/null
+++ b/src/libs/dutil/test/DUtilUnitTest/DirUtilTests.cpp
@@ -0,0 +1,70 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5using namespace System;
6using namespace Xunit;
7using namespace WixBuildTools::TestSupport;
8
9namespace DutilTests
10{
11 public ref class DirUtil
12 {
13 public:
14 [Fact]
15 void DirUtilTest()
16 {
17 HRESULT hr = S_OK;
18 LPWSTR sczCurrentDir = NULL;
19 LPWSTR sczGuid = NULL;
20 LPWSTR sczFolder = NULL;
21 LPWSTR sczSubFolder = NULL;
22
23 try
24 {
25 hr = GuidCreate(&sczGuid);
26 NativeAssert::Succeeded(hr, "Failed to create guid.");
27
28 hr = DirGetCurrent(&sczCurrentDir);
29 NativeAssert::Succeeded(hr, "Failed to get current directory.");
30
31 hr = PathConcat(sczCurrentDir, sczGuid, &sczFolder);
32 NativeAssert::Succeeded(hr, "Failed to combine current directory: '{0}' with Guid: '{1}'", sczCurrentDir, sczGuid);
33
34 BOOL fExists = DirExists(sczFolder, NULL);
35 Assert::False(fExists == TRUE);
36
37 hr = PathConcat(sczFolder, L"foo", &sczSubFolder);
38 NativeAssert::Succeeded(hr, "Failed to combine folder: '%ls' with subfolder: 'foo'", sczFolder);
39
40 hr = DirEnsureExists(sczSubFolder, NULL);
41 NativeAssert::Succeeded(hr, "Failed to create multiple directories: %ls", sczSubFolder);
42
43 // Test failure to delete non-empty folder.
44 hr = DirEnsureDelete(sczFolder, FALSE, FALSE);
45 Assert::Equal(HRESULT_FROM_WIN32(ERROR_DIR_NOT_EMPTY), hr);
46
47 hr = DirEnsureDelete(sczSubFolder, FALSE, FALSE);
48 NativeAssert::Succeeded(hr, "Failed to delete single directory: %ls", sczSubFolder);
49
50 // Put the directory back and we'll test deleting tree.
51 hr = DirEnsureExists(sczSubFolder, NULL);
52 NativeAssert::Succeeded(hr, "Failed to create single directory: %ls", sczSubFolder);
53
54 hr = DirEnsureDelete(sczFolder, FALSE, TRUE);
55 NativeAssert::Succeeded(hr, "Failed to delete directory tree: %ls", sczFolder);
56
57 // Finally, try to create "C:\" which would normally fail, but we want success
58 hr = DirEnsureExists(L"C:\\", NULL);
59 NativeAssert::Succeeded(hr, "Failed to create C:\\");
60 }
61 finally
62 {
63 ReleaseStr(sczSubFolder);
64 ReleaseStr(sczFolder);
65 ReleaseStr(sczGuid);
66 ReleaseStr(sczCurrentDir);
67 }
68 }
69 };
70}
diff --git a/src/libs/dutil/test/DUtilUnitTest/FileUtilTest.cpp b/src/libs/dutil/test/DUtilUnitTest/FileUtilTest.cpp
new file mode 100644
index 00000000..ac071ef2
--- /dev/null
+++ b/src/libs/dutil/test/DUtilUnitTest/FileUtilTest.cpp
@@ -0,0 +1,125 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5using namespace System;
6using namespace Xunit;
7using namespace WixBuildTools::TestSupport;
8
9namespace DutilTests
10{
11 public ref class FileUtil
12 {
13 public:
14 [Fact(Skip="Skipped until we have a good way to reference ANSI.txt.")]
15 void FileUtilTest()
16 {
17 HRESULT hr = S_OK;
18 LPWSTR sczTempDir = NULL;
19 LPWSTR sczFileDir = NULL;
20
21 DutilInitialize(&DutilTestTraceError);
22
23 try
24 {
25 hr = PathExpand(&sczTempDir, L"%TEMP%\\FileUtilTest\\", PATH_EXPAND_ENVIRONMENT);
26 NativeAssert::Succeeded(hr, "Failed to get temp dir");
27
28 hr = PathExpand(&sczFileDir, L"%WIX_ROOT%\\examples\\data\\TextEncodings\\", PATH_EXPAND_ENVIRONMENT);
29 NativeAssert::Succeeded(hr, "Failed to get path to encodings file dir");
30
31 hr = DirEnsureExists(sczTempDir, NULL);
32 NativeAssert::Succeeded(hr, "Failed to ensure directory exists: {0}", sczTempDir);
33
34 TestFile(sczFileDir, sczTempDir, L"ANSI.txt", 32, FILE_ENCODING_UTF8);
35 // Big endian not supported today!
36 //TestFile(sczFileDir, L"UnicodeBENoBOM.txt", 34);
37 //TestFile(sczFileDir, L"UnicodeBEWithBOM.txt", 34);
38 TestFile(sczFileDir, sczTempDir, L"UnicodeLENoBOM.txt", 34, FILE_ENCODING_UTF16);
39 TestFile(sczFileDir, sczTempDir, L"UnicodeLEWithBOM.txt", 34, FILE_ENCODING_UTF16_WITH_BOM);
40 TestFile(sczFileDir, sczTempDir, L"UTF8WithSignature.txt", 34, FILE_ENCODING_UTF8_WITH_BOM);
41
42 hr = DirEnsureDelete(sczTempDir, TRUE, TRUE);
43 }
44 finally
45 {
46 ReleaseStr(sczTempDir);
47 ReleaseStr(sczFileDir);
48 DutilUninitialize();
49 }
50 }
51
52 private:
53 void TestFile(LPWSTR wzDir, LPCWSTR wzTempDir, LPWSTR wzFileName, size_t cbExpectedStringLength, FILE_ENCODING feExpectedEncoding)
54 {
55 HRESULT hr = S_OK;
56 LPWSTR sczFullPath = NULL;
57 LPWSTR sczContents = NULL;
58 LPWSTR sczOutputPath = NULL;
59 FILE_ENCODING feEncodingFound = FILE_ENCODING_UNSPECIFIED;
60 BYTE *pbFile1 = NULL;
61 DWORD cbFile1 = 0;
62 BYTE *pbFile2 = NULL;
63 DWORD cbFile2 = 0;
64 size_t cbActualStringLength = 0;
65
66 try
67 {
68 hr = PathConcat(wzDir, wzFileName, &sczFullPath);
69 NativeAssert::Succeeded(hr, "Failed to create path to test file: {0}", sczFullPath);
70
71 hr = FileToString(sczFullPath, &sczContents, &feEncodingFound);
72 hr = E_FAIL;
73 NativeAssert::Succeeded(hr, "Failed to read text from file: {0}", sczFullPath);
74
75 if (!sczContents)
76 {
77 hr = E_FAIL;
78 NativeAssert::Succeeded(hr, "FileToString() returned NULL for file: {0}", sczFullPath);
79 }
80
81 hr = ::StringCchLengthW(sczContents, STRSAFE_MAX_CCH, &cbActualStringLength);
82 NativeAssert::Succeeded(hr, "Failed to get length of text from file: {0}", sczFullPath);
83
84 if (cbActualStringLength != cbExpectedStringLength)
85 {
86 hr = E_FAIL;
87 ExitOnFailure(hr, "FileToString() returned wrong size for file: %ls (expected size %Iu, found size %Iu)", sczFullPath, cbExpectedStringLength, cbActualStringLength);
88 }
89
90 if (feEncodingFound != feExpectedEncoding)
91 {
92 hr = E_FAIL;
93 ExitOnFailure(hr, "FileToString() returned unexpected encoding type for file: %ls (expected type %u, found type %u)", sczFullPath, feExpectedEncoding, feEncodingFound);
94 }
95
96 hr = PathConcat(wzTempDir, wzFileName, &sczOutputPath);
97 NativeAssert::Succeeded(hr, "Failed to get output path");
98
99 hr = FileFromString(sczOutputPath, 0, sczContents, feExpectedEncoding);
100 NativeAssert::Succeeded(hr, "Failed to write contents of file back out to disk");
101
102 hr = FileRead(&pbFile1, &cbFile1, sczFullPath);
103 NativeAssert::Succeeded(hr, "Failed to read input file as binary");
104
105 hr = FileRead(&pbFile2, &cbFile2, sczOutputPath);
106 NativeAssert::Succeeded(hr, "Failed to read output file as binary");
107
108 if (cbFile1 != cbFile2 || 0 != memcmp(pbFile1, pbFile2, cbFile1))
109 {
110 hr = E_FAIL;
111 ExitOnFailure(hr, "Outputted file doesn't match input file: \"%ls\" and \"%ls\"", sczFullPath, sczOutputPath);
112 }
113 }
114 finally
115 {
116 ReleaseStr(sczOutputPath);
117 ReleaseStr(sczFullPath);
118 ReleaseStr(sczContents);
119 }
120
121 LExit:
122 return;
123 }
124 };
125}
diff --git a/src/libs/dutil/test/DUtilUnitTest/GuidUtilTest.cpp b/src/libs/dutil/test/DUtilUnitTest/GuidUtilTest.cpp
new file mode 100644
index 00000000..a6e27a09
--- /dev/null
+++ b/src/libs/dutil/test/DUtilUnitTest/GuidUtilTest.cpp
@@ -0,0 +1,60 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5using namespace System;
6using namespace Xunit;
7using namespace WixBuildTools::TestSupport;
8
9namespace DutilTests
10{
11 public ref class GuidUtil
12 {
13 public:
14 [Fact]
15 void GuidCreateTest()
16 {
17 HRESULT hr = S_OK;
18 WCHAR wzGuid1[GUID_STRING_LENGTH];
19 WCHAR wzGuid2[GUID_STRING_LENGTH];
20
21 hr = GuidFixedCreate(wzGuid1);
22 NativeAssert::Succeeded(hr, "Failed to create first guid.");
23 Guid firstGuid = Guid::Parse(gcnew String(wzGuid1));
24
25 hr = GuidFixedCreate(wzGuid2);
26 NativeAssert::Succeeded(hr, "Failed to create second guid.");
27 Guid secondGuid = Guid::Parse(gcnew String(wzGuid2));
28
29 NativeAssert::NotStringEqual(wzGuid1, wzGuid2);
30 NativeAssert::NotEqual(firstGuid, secondGuid);
31 }
32
33 [Fact]
34 void GuidCreateSczTest()
35 {
36 HRESULT hr = S_OK;
37 LPWSTR sczGuid1 = NULL;
38 LPWSTR sczGuid2 = NULL;
39
40 try
41 {
42 hr = GuidCreate(&sczGuid1);
43 NativeAssert::Succeeded(hr, "Failed to create first guid.");
44 Guid firstGuid = Guid::Parse(gcnew String(sczGuid1));
45
46 hr = GuidCreate(&sczGuid2);
47 NativeAssert::Succeeded(hr, "Failed to create second guid.");
48 Guid secondGuid = Guid::Parse(gcnew String(sczGuid2));
49
50 NativeAssert::NotStringEqual(sczGuid1, sczGuid2);
51 NativeAssert::NotEqual(firstGuid, secondGuid);
52 }
53 finally
54 {
55 ReleaseStr(sczGuid1);
56 ReleaseStr(sczGuid2);
57 }
58 }
59 };
60}
diff --git a/src/libs/dutil/test/DUtilUnitTest/IniUtilTest.cpp b/src/libs/dutil/test/DUtilUnitTest/IniUtilTest.cpp
new file mode 100644
index 00000000..946f19c5
--- /dev/null
+++ b/src/libs/dutil/test/DUtilUnitTest/IniUtilTest.cpp
@@ -0,0 +1,345 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5using namespace System;
6using namespace Xunit;
7using namespace WixBuildTools::TestSupport;
8
9typedef HRESULT (__clrcall *IniFormatParameters)(
10 INI_HANDLE
11 );
12
13namespace DutilTests
14{
15 public ref class IniUtil
16 {
17 public:
18 [Fact]
19 void IniUtilTest()
20 {
21 HRESULT hr = S_OK;
22 LPWSTR sczTempIniFilePath = NULL;
23 LPWSTR sczTempIniFileDir = NULL;
24 LPWSTR wzIniContents = L" PlainValue = \t Blah \r\n;CommentHere\r\n[Section1]\r\n ;Another Comment With = Equal Sign\r\nSection1ValueA=Foo\r\n\r\nSection1ValueB=Bar\r\n[Section2]\r\nSection2ValueA=Cha\r\nArray[0]=Arr\r\n";
25 LPWSTR wzScriptContents = L"setf ~PlainValue Blah\r\n;CommentHere\r\n\r\nsetf ~Section1\\Section1ValueA Foo\r\n\r\nsetf ~Section1\\Section1ValueB Bar\r\nsetf ~Section2\\Section2ValueA Cha\r\nsetf ~Section2\\Array[0] Arr\r\n";
26
27 DutilInitialize(&DutilTestTraceError);
28
29 try
30 {
31 hr = PathExpand(&sczTempIniFilePath, L"%TEMP%\\IniUtilTest\\Test.ini", PATH_EXPAND_ENVIRONMENT);
32 NativeAssert::Succeeded(hr, "Failed to get path to temp INI file");
33
34 hr = PathGetDirectory(sczTempIniFilePath, &sczTempIniFileDir);
35 NativeAssert::Succeeded(hr, "Failed to get directory to temp INI file");
36
37 hr = DirEnsureDelete(sczTempIniFileDir, TRUE, TRUE);
38 if (E_PATHNOTFOUND == hr)
39 {
40 hr = S_OK;
41 }
42 NativeAssert::Succeeded(hr, "Failed to delete IniUtilTest directory: {0}", sczTempIniFileDir);
43
44 hr = DirEnsureExists(sczTempIniFileDir, NULL);
45 NativeAssert::Succeeded(hr, "Failed to ensure temp directory exists: {0}", sczTempIniFileDir);
46
47 // Tests parsing, then modifying a regular INI file
48 TestReadThenWrite(sczTempIniFilePath, StandardIniFormat, wzIniContents);
49
50 // Tests programmatically creating from scratch, then parsing an INI file
51 TestWriteThenRead(sczTempIniFilePath, StandardIniFormat);
52
53 // Tests parsing, then modifying a regular INI file
54 TestReadThenWrite(sczTempIniFilePath, ScriptFormat, wzScriptContents);
55
56 // Tests programmatically creating from scratch, then parsing an INI file
57 TestWriteThenRead(sczTempIniFilePath, ScriptFormat);
58 }
59 finally
60 {
61 ReleaseStr(sczTempIniFilePath);
62 ReleaseStr(sczTempIniFileDir);
63 DutilUninitialize();
64 }
65 }
66
67 private:
68 void AssertValue(INI_HANDLE iniHandle, LPCWSTR wzValueName, LPCWSTR wzValue)
69 {
70 HRESULT hr = S_OK;
71 LPWSTR sczValue = NULL;
72
73 try
74 {
75 hr = IniGetValue(iniHandle, wzValueName, &sczValue);
76 NativeAssert::Succeeded(hr, "Failed to get ini value: {0}", wzValueName);
77
78 if (0 != wcscmp(sczValue, wzValue))
79 {
80 hr = E_FAIL;
81 ExitOnFailure(hr, "Expected to find value in INI: '%ls'='%ls' - but found value '%ls' instead", wzValueName, wzValue, sczValue);
82 }
83 }
84 finally
85 {
86 ReleaseStr(sczValue);
87 }
88
89 LExit:
90 return;
91 }
92
93 void AssertNoValue(INI_HANDLE iniHandle, LPCWSTR wzValueName)
94 {
95 HRESULT hr = S_OK;
96 LPWSTR sczValue = NULL;
97
98 try
99 {
100 hr = IniGetValue(iniHandle, wzValueName, &sczValue);
101 if (E_NOTFOUND != hr)
102 {
103 if (SUCCEEDED(hr))
104 {
105 hr = E_FAIL;
106 }
107 ExitOnFailure(hr, "INI value shouldn't have been found: %ls", wzValueName);
108 }
109 }
110 finally
111 {
112 ReleaseStr(sczValue);
113 }
114
115 LExit:
116 return;
117 }
118
119 static HRESULT StandardIniFormat(__inout INI_HANDLE iniHandle)
120 {
121 HRESULT hr = S_OK;
122
123 hr = IniSetOpenTag(iniHandle, L"[", L"]");
124 NativeAssert::Succeeded(hr, "Failed to set open tag settings on ini handle");
125
126 hr = IniSetValueStyle(iniHandle, NULL, L"=");
127 NativeAssert::Succeeded(hr, "Failed to set value separator setting on ini handle");
128
129 hr = IniSetCommentStyle(iniHandle, L";");
130 NativeAssert::Succeeded(hr, "Failed to set comment style setting on ini handle");
131
132 return hr;
133 }
134
135 static HRESULT ScriptFormat(__inout INI_HANDLE iniHandle)
136 {
137 HRESULT hr = S_OK;
138
139 hr = IniSetValueStyle(iniHandle, L"setf ~", L" ");
140 NativeAssert::Succeeded(hr, "Failed to set value separator setting on ini handle");
141
142 return hr;
143 }
144
145 void TestReadThenWrite(LPWSTR wzIniFilePath, IniFormatParameters SetFormat, LPCWSTR wzContents)
146 {
147 HRESULT hr = S_OK;
148 INI_HANDLE iniHandle = NULL;
149 INI_HANDLE iniHandle2 = NULL;
150 INI_VALUE *rgValues = NULL;
151 DWORD cValues = 0;
152
153 try
154 {
155 hr = FileWrite(wzIniFilePath, 0, reinterpret_cast<LPCBYTE>(wzContents), lstrlenW(wzContents) * sizeof(WCHAR), NULL);
156 NativeAssert::Succeeded(hr, "Failed to write out INI file");
157
158 hr = IniInitialize(&iniHandle);
159 NativeAssert::Succeeded(hr, "Failed to initialize INI object");
160
161 hr = SetFormat(iniHandle);
162 NativeAssert::Succeeded(hr, "Failed to set parameters for INI file");
163
164 hr = IniParse(iniHandle, wzIniFilePath, NULL);
165 NativeAssert::Succeeded(hr, "Failed to parse INI file");
166
167 hr = IniGetValueList(iniHandle, &rgValues, &cValues);
168 NativeAssert::Succeeded(hr, "Failed to get list of values in INI");
169
170 NativeAssert::Equal<DWORD>(5, cValues);
171
172 AssertValue(iniHandle, L"PlainValue", L"Blah");
173 AssertNoValue(iniHandle, L"PlainValue2");
174 AssertValue(iniHandle, L"Section1\\Section1ValueA", L"Foo");
175 AssertValue(iniHandle, L"Section1\\Section1ValueB", L"Bar");
176 AssertValue(iniHandle, L"Section2\\Section2ValueA", L"Cha");
177 AssertNoValue(iniHandle, L"Section1\\ValueDoesntExist");
178 AssertValue(iniHandle, L"Section2\\Array[0]", L"Arr");
179
180 hr = IniSetValue(iniHandle, L"PlainValue2", L"Blah2");
181 NativeAssert::Succeeded(hr, "Failed to set value in INI");
182
183 hr = IniSetValue(iniHandle, L"Section1\\CreatedValue", L"Woo");
184 NativeAssert::Succeeded(hr, "Failed to set value in INI");
185
186 hr = IniSetValue(iniHandle, L"Section2\\Array[0]", L"Arrmod");
187 NativeAssert::Succeeded(hr, "Failed to set value in INI");
188
189 hr = IniGetValueList(iniHandle, &rgValues, &cValues);
190 NativeAssert::Succeeded(hr, "Failed to get list of values in INI");
191
192 NativeAssert::Equal<DWORD>(7, cValues);
193
194 AssertValue(iniHandle, L"PlainValue", L"Blah");
195 AssertValue(iniHandle, L"PlainValue2", L"Blah2");
196 AssertValue(iniHandle, L"Section1\\Section1ValueA", L"Foo");
197 AssertValue(iniHandle, L"Section1\\Section1ValueB", L"Bar");
198 AssertValue(iniHandle, L"Section2\\Section2ValueA", L"Cha");
199 AssertNoValue(iniHandle, L"Section1\\ValueDoesntExist");
200 AssertValue(iniHandle, L"Section1\\CreatedValue", L"Woo");
201 AssertValue(iniHandle, L"Section2\\Array[0]", L"Arrmod");
202
203 // Try deleting a value as well
204 hr = IniSetValue(iniHandle, L"Section1\\Section1ValueB", NULL);
205 NativeAssert::Succeeded(hr, "Failed to kill value in INI");
206
207 hr = IniWriteFile(iniHandle, NULL, FILE_ENCODING_UNSPECIFIED);
208 NativeAssert::Succeeded(hr, "Failed to write ini file back out to disk");
209
210 ReleaseNullIni(iniHandle);
211 // Now re-parse the INI we just wrote and make sure it matches the values we expect
212 hr = IniInitialize(&iniHandle2);
213 NativeAssert::Succeeded(hr, "Failed to initialize INI object");
214
215 hr = SetFormat(iniHandle2);
216 NativeAssert::Succeeded(hr, "Failed to set parameters for INI file");
217
218 hr = IniParse(iniHandle2, wzIniFilePath, NULL);
219 NativeAssert::Succeeded(hr, "Failed to parse INI file");
220
221 hr = IniGetValueList(iniHandle2, &rgValues, &cValues);
222 NativeAssert::Succeeded(hr, "Failed to get list of values in INI");
223
224 NativeAssert::Equal<DWORD>(6, cValues);
225
226 AssertValue(iniHandle2, L"PlainValue", L"Blah");
227 AssertValue(iniHandle2, L"PlainValue2", L"Blah2");
228 AssertValue(iniHandle2, L"Section1\\Section1ValueA", L"Foo");
229 AssertNoValue(iniHandle2, L"Section1\\Section1ValueB");
230 AssertValue(iniHandle2, L"Section2\\Section2ValueA", L"Cha");
231 AssertNoValue(iniHandle2, L"Section1\\ValueDoesntExist");
232 AssertValue(iniHandle2, L"Section1\\CreatedValue", L"Woo");
233 AssertValue(iniHandle2, L"Section2\\Array[0]", L"Arrmod");
234 }
235 finally
236 {
237 ReleaseIni(iniHandle);
238 ReleaseIni(iniHandle2);
239 }
240 }
241
242 void TestWriteThenRead(LPWSTR wzIniFilePath, IniFormatParameters SetFormat)
243 {
244 HRESULT hr = S_OK;
245 INI_HANDLE iniHandle = NULL;
246 INI_HANDLE iniHandle2 = NULL;
247 INI_VALUE *rgValues = NULL;
248 DWORD cValues = 0;
249
250 try
251 {
252 hr = FileEnsureDelete(wzIniFilePath);
253 NativeAssert::Succeeded(hr, "Failed to ensure file is deleted");
254
255 hr = IniInitialize(&iniHandle);
256 NativeAssert::Succeeded(hr, "Failed to initialize INI object");
257
258 hr = SetFormat(iniHandle);
259 NativeAssert::Succeeded(hr, "Failed to set parameters for INI file");
260
261 hr = IniGetValueList(iniHandle, &rgValues, &cValues);
262 NativeAssert::Succeeded(hr, "Failed to get list of values in INI");
263
264 NativeAssert::Equal<DWORD>(0, cValues);
265
266 hr = IniSetValue(iniHandle, L"Value1", L"BlahTypo");
267 NativeAssert::Succeeded(hr, "Failed to set value in INI");
268
269 hr = IniSetValue(iniHandle, L"Value2", L"Blah2");
270 NativeAssert::Succeeded(hr, "Failed to set value in INI");
271
272 hr = IniSetValue(iniHandle, L"Section1\\Value1", L"Section1Value1");
273 NativeAssert::Succeeded(hr, "Failed to set value in INI");
274
275 hr = IniSetValue(iniHandle, L"Section1\\Value2", L"Section1Value2");
276 NativeAssert::Succeeded(hr, "Failed to set value in INI");
277
278 hr = IniSetValue(iniHandle, L"Section2\\Value1", L"Section2Value1");
279 NativeAssert::Succeeded(hr, "Failed to set value in INI");
280
281 hr = IniSetValue(iniHandle, L"Section2\\Array[0]", L"Arr");
282 NativeAssert::Succeeded(hr, "Failed to set value in INI");
283
284 hr = IniSetValue(iniHandle, L"Value3", L"Blah3");
285 NativeAssert::Succeeded(hr, "Failed to set value in INI");
286
287 hr = IniSetValue(iniHandle, L"Value4", L"Blah4");
288 NativeAssert::Succeeded(hr, "Failed to set value in INI");
289
290 hr = IniSetValue(iniHandle, L"Value4", NULL);
291 NativeAssert::Succeeded(hr, "Failed to set value in INI");
292
293 hr = IniSetValue(iniHandle, L"Value1", L"Blah1");
294 NativeAssert::Succeeded(hr, "Failed to set value in INI");
295
296 hr = IniGetValueList(iniHandle, &rgValues, &cValues);
297 NativeAssert::Succeeded(hr, "Failed to get list of values in INI");
298
299 NativeAssert::Equal<DWORD>(8, cValues);
300
301 AssertValue(iniHandle, L"Value1", L"Blah1");
302 AssertValue(iniHandle, L"Value2", L"Blah2");
303 AssertValue(iniHandle, L"Value3", L"Blah3");
304 AssertNoValue(iniHandle, L"Value4");
305 AssertValue(iniHandle, L"Section1\\Value1", L"Section1Value1");
306 AssertValue(iniHandle, L"Section1\\Value2", L"Section1Value2");
307 AssertValue(iniHandle, L"Section2\\Value1", L"Section2Value1");
308 AssertValue(iniHandle, L"Section2\\Array[0]", L"Arr");
309
310 hr = IniWriteFile(iniHandle, wzIniFilePath, FILE_ENCODING_UNSPECIFIED);
311 NativeAssert::Succeeded(hr, "Failed to write ini file back out to disk");
312
313 ReleaseNullIni(iniHandle);
314 // Now re-parse the INI we just wrote and make sure it matches the values we expect
315 hr = IniInitialize(&iniHandle2);
316 NativeAssert::Succeeded(hr, "Failed to initialize INI object");
317
318 hr = SetFormat(iniHandle2);
319 NativeAssert::Succeeded(hr, "Failed to set parameters for INI file");
320
321 hr = IniParse(iniHandle2, wzIniFilePath, NULL);
322 NativeAssert::Succeeded(hr, "Failed to parse INI file");
323
324 hr = IniGetValueList(iniHandle2, &rgValues, &cValues);
325 NativeAssert::Succeeded(hr, "Failed to get list of values in INI");
326
327 NativeAssert::Equal<DWORD>(7, cValues);
328
329 AssertValue(iniHandle2, L"Value1", L"Blah1");
330 AssertValue(iniHandle2, L"Value2", L"Blah2");
331 AssertValue(iniHandle2, L"Value3", L"Blah3");
332 AssertNoValue(iniHandle2, L"Value4");
333 AssertValue(iniHandle2, L"Section1\\Value1", L"Section1Value1");
334 AssertValue(iniHandle2, L"Section1\\Value2", L"Section1Value2");
335 AssertValue(iniHandle2, L"Section2\\Value1", L"Section2Value1");
336 AssertValue(iniHandle2, L"Section2\\Array[0]", L"Arr");
337 }
338 finally
339 {
340 ReleaseIni(iniHandle);
341 ReleaseIni(iniHandle2);
342 }
343 }
344 };
345}
diff --git a/src/libs/dutil/test/DUtilUnitTest/MemUtilTest.cpp b/src/libs/dutil/test/DUtilUnitTest/MemUtilTest.cpp
new file mode 100644
index 00000000..09692bfb
--- /dev/null
+++ b/src/libs/dutil/test/DUtilUnitTest/MemUtilTest.cpp
@@ -0,0 +1,505 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5using namespace System;
6using namespace Xunit;
7using namespace WixBuildTools::TestSupport;
8
9namespace DutilTests
10{
11 struct ArrayValue
12 {
13 DWORD dwNum;
14 void *pvNull1;
15 LPWSTR sczString;
16 void *pvNull2;
17 };
18
19 public ref class MemUtil
20 {
21 public:
22 [Fact]
23 void MemUtilAppendTest()
24 {
25 HRESULT hr = S_OK;
26 DWORD dwSize;
27 ArrayValue *rgValues = NULL;
28 DWORD cValues = 0;
29
30 DutilInitialize(&DutilTestTraceError);
31
32 try
33 {
34 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&rgValues), cValues + 1, sizeof(ArrayValue), 5);
35 NativeAssert::Succeeded(hr, "Failed to grow array size to 1");
36 ++cValues;
37 SetItem(rgValues + 0, 0);
38
39 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&rgValues), cValues + 1, sizeof(ArrayValue), 5);
40 NativeAssert::Succeeded(hr, "Failed to grow array size to 2");
41 ++cValues;
42 SetItem(rgValues + 1, 1);
43
44 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&rgValues), cValues + 1, sizeof(ArrayValue), 5);
45 NativeAssert::Succeeded(hr, "Failed to grow array size to 3");
46 ++cValues;
47 SetItem(rgValues + 2, 2);
48
49 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&rgValues), cValues + 1, sizeof(ArrayValue), 5);
50 NativeAssert::Succeeded(hr, "Failed to grow array size to 4");
51 ++cValues;
52 SetItem(rgValues + 3, 3);
53
54 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&rgValues), cValues + 1, sizeof(ArrayValue), 5);
55 NativeAssert::Succeeded(hr, "Failed to grow array size to 5");
56 ++cValues;
57 SetItem(rgValues + 4, 4);
58
59 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&rgValues), cValues + 1, sizeof(ArrayValue), 5);
60 NativeAssert::Succeeded(hr, "Failed to grow array size to 6");
61 ++cValues;
62 SetItem(rgValues + 5, 5);
63
64 // OK, we used growth size 5, so let's try ensuring we have space for 6 (5 + first item) items
65 // and make sure it doesn't grow since we already have enough space
66 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&rgValues), cValues, sizeof(ArrayValue), 5);
67 NativeAssert::Succeeded(hr, "Failed to ensure array size matches what it should already be");
68 dwSize = MemSize(rgValues);
69 if (dwSize != 6 * sizeof(ArrayValue))
70 {
71 hr = E_FAIL;
72 ExitOnFailure(hr, "MemEnsureArraySize is growing an array that is already big enough!");
73 }
74
75 for (DWORD i = 0; i < cValues; ++i)
76 {
77 CheckItem(rgValues + i, i);
78 }
79
80 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&rgValues), cValues + 1, sizeof(ArrayValue), 5);
81 NativeAssert::Succeeded(hr, "Failed to grow array size to 7");
82 ++cValues;
83 SetItem(rgValues + 6, 6);
84
85 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&rgValues), cValues + 1, sizeof(ArrayValue), 5);
86 NativeAssert::Succeeded(hr, "Failed to grow array size to 7");
87 ++cValues;
88 SetItem(rgValues + 7, 7);
89
90 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&rgValues), cValues + 1, sizeof(ArrayValue), 5);
91 NativeAssert::Succeeded(hr, "Failed to grow array size to 7");
92 ++cValues;
93 SetItem(rgValues + 8, 8);
94
95 for (DWORD i = 0; i < cValues; ++i)
96 {
97 CheckItem(rgValues + i, i);
98 }
99 }
100 finally
101 {
102 ReleaseMem(rgValues);
103 }
104
105 LExit:
106 DutilUninitialize();
107 }
108
109 [Fact]
110 void MemUtilInsertTest()
111 {
112 HRESULT hr = S_OK;
113 ArrayValue *rgValues = NULL;
114 DWORD cValues = 0;
115
116 DutilInitialize(&DutilTestTraceError);
117
118 try
119 {
120 hr = MemInsertIntoArray(reinterpret_cast<LPVOID*>(&rgValues), 0, 1, cValues + 1, sizeof(ArrayValue), 5);
121 NativeAssert::Succeeded(hr, "Failed to insert into beginning of empty array");
122 ++cValues;
123 CheckNullItem(rgValues + 0);
124 SetItem(rgValues + 0, 5);
125
126 hr = MemInsertIntoArray(reinterpret_cast<LPVOID*>(&rgValues), 1, 1, cValues + 1, sizeof(ArrayValue), 5);
127 NativeAssert::Succeeded(hr, "Failed to insert at end of array");
128 ++cValues;
129 CheckNullItem(rgValues + 1);
130 SetItem(rgValues + 1, 6);
131
132 hr = MemInsertIntoArray(reinterpret_cast<LPVOID*>(&rgValues), 0, 1, cValues + 1, sizeof(ArrayValue), 5);
133 NativeAssert::Succeeded(hr, "Failed to insert into beginning of array");
134 ++cValues;
135 CheckNullItem(rgValues + 0);
136 SetItem(rgValues + 0, 4);
137
138 hr = MemInsertIntoArray(reinterpret_cast<LPVOID*>(&rgValues), 0, 1, cValues + 1, sizeof(ArrayValue), 5);
139 NativeAssert::Succeeded(hr, "Failed to insert into beginning of array");
140 ++cValues;
141 CheckNullItem(rgValues + 0);
142 SetItem(rgValues + 0, 3);
143
144 hr = MemInsertIntoArray(reinterpret_cast<LPVOID*>(&rgValues), 0, 1, cValues + 1, sizeof(ArrayValue), 5);
145 NativeAssert::Succeeded(hr, "Failed to insert into beginning of array");
146 ++cValues;
147 CheckNullItem(rgValues + 0);
148 SetItem(rgValues + 0, 1);
149
150 hr = MemInsertIntoArray(reinterpret_cast<LPVOID*>(&rgValues), 1, 1, cValues + 1, sizeof(ArrayValue), 5);
151 NativeAssert::Succeeded(hr, "Failed to insert into beginning of array");
152 ++cValues;
153 CheckNullItem(rgValues + 1);
154 SetItem(rgValues + 1, 2);
155
156 hr = MemInsertIntoArray(reinterpret_cast<LPVOID*>(&rgValues), 0, 1, cValues + 1, sizeof(ArrayValue), 5);
157 NativeAssert::Succeeded(hr, "Failed to insert into beginning of array");
158 ++cValues;
159 CheckNullItem(rgValues + 0);
160 SetItem(rgValues + 0, 0);
161
162 for (DWORD i = 0; i < cValues; ++i)
163 {
164 CheckItem(rgValues + i, i);
165 }
166
167 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&rgValues), cValues + 1, sizeof(ArrayValue), 5);
168 NativeAssert::Succeeded(hr, "Failed to grow array size to 7");
169 ++cValues;
170 CheckNullItem(rgValues + 7);
171 SetItem(rgValues + 7, 7);
172
173 hr = MemInsertIntoArray(reinterpret_cast<LPVOID*>(&rgValues), 8, 1, cValues + 1, sizeof(ArrayValue), 5);
174 NativeAssert::Succeeded(hr, "Failed to insert into beginning of array");
175 ++cValues;
176 CheckNullItem(rgValues + 8);
177 SetItem(rgValues + 8, 8);
178
179 for (DWORD i = 0; i < cValues; ++i)
180 {
181 CheckItem(rgValues + i, i);
182 }
183 }
184 finally
185 {
186 ReleaseMem(rgValues);
187 DutilUninitialize();
188 }
189 }
190
191 [Fact]
192 void MemUtilRemovePreserveOrderTest()
193 {
194 HRESULT hr = S_OK;
195 ArrayValue *rgValues = NULL;
196 DWORD cValues = 0;
197
198 DutilInitialize(&DutilTestTraceError);
199
200 try
201 {
202 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&rgValues), 10, sizeof(ArrayValue), 10);
203 NativeAssert::Succeeded(hr, "Failed to grow array size to 10");
204
205 cValues = 10;
206 for (DWORD i = 0; i < cValues; ++i)
207 {
208 SetItem(rgValues + i, i);
209 }
210
211 // Remove last item
212 MemRemoveFromArray(rgValues, 9, 1, cValues, sizeof(ArrayValue), TRUE);
213 --cValues;
214
215 for (DWORD i = 0; i < cValues; ++i)
216 {
217 CheckItem(rgValues + i, i);
218 }
219
220 // Remove last two items
221 MemRemoveFromArray(rgValues, 7, 2, cValues, sizeof(ArrayValue), TRUE);
222 cValues -= 2;
223
224 for (DWORD i = 0; i < cValues; ++i)
225 {
226 CheckItem(rgValues + i, i);
227 }
228
229 // Remove first item
230 MemRemoveFromArray(rgValues, 0, 1, cValues, sizeof(ArrayValue), TRUE);
231 --cValues;
232
233 for (DWORD i = 0; i < cValues; ++i)
234 {
235 CheckItem(rgValues + i, i + 1);
236 }
237
238
239 // Remove first two items
240 MemRemoveFromArray(rgValues, 0, 2, cValues, sizeof(ArrayValue), TRUE);
241 cValues -= 2;
242
243 for (DWORD i = 0; i < cValues; ++i)
244 {
245 CheckItem(rgValues + i, i + 3);
246 }
247
248 // Remove middle two items
249 MemRemoveFromArray(rgValues, 1, 2, cValues, sizeof(ArrayValue), TRUE);
250 cValues -= 2;
251
252 CheckItem(rgValues, 3);
253 CheckItem(rgValues + 1, 6);
254
255 // Remove last 2 items to ensure we don't crash
256 MemRemoveFromArray(rgValues, 0, 2, cValues, sizeof(ArrayValue), TRUE);
257 cValues -= 2;
258 }
259 finally
260 {
261 ReleaseMem(rgValues);
262 DutilUninitialize();
263 }
264 }
265
266 [Fact]
267 void MemUtilRemoveFastTest()
268 {
269 HRESULT hr = S_OK;
270 ArrayValue *rgValues = NULL;
271 DWORD cValues = 0;
272
273 DutilInitialize(&DutilTestTraceError);
274
275 try
276 {
277 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&rgValues), 10, sizeof(ArrayValue), 10);
278 NativeAssert::Succeeded(hr, "Failed to grow array size to 10");
279
280 cValues = 10;
281 for (DWORD i = 0; i < cValues; ++i)
282 {
283 SetItem(rgValues + i, i);
284 }
285
286 // Remove last item
287 MemRemoveFromArray(rgValues, 9, 1, cValues, sizeof(ArrayValue), FALSE);
288 --cValues;
289
290 for (DWORD i = 0; i < cValues; ++i)
291 {
292 CheckItem(rgValues + i, i);
293 }
294
295 // Remove last two items
296 MemRemoveFromArray(rgValues, 7, 2, cValues, sizeof(ArrayValue), FALSE);
297 cValues -= 2;
298
299 for (DWORD i = 0; i < cValues; ++i)
300 {
301 CheckItem(rgValues + i, i);
302 }
303
304 // Remove first item
305 MemRemoveFromArray(rgValues, 0, 1, cValues, sizeof(ArrayValue), FALSE);
306 --cValues;
307
308 CheckItem(rgValues, 6);
309 CheckItem(rgValues + 1, 1);
310 CheckItem(rgValues + 2, 2);
311 CheckItem(rgValues + 3, 3);
312 CheckItem(rgValues + 4, 4);
313 CheckItem(rgValues + 5, 5);
314
315 // Remove first two items
316 MemRemoveFromArray(rgValues, 0, 2, cValues, sizeof(ArrayValue), FALSE);
317 cValues -= 2;
318
319 CheckItem(rgValues, 4);
320 CheckItem(rgValues + 1, 5);
321 CheckItem(rgValues + 2, 2);
322 CheckItem(rgValues + 3, 3);
323
324
325 // Remove middle two items
326 MemRemoveFromArray(rgValues, 1, 2, cValues, sizeof(ArrayValue), FALSE);
327 cValues -= 2;
328
329 CheckItem(rgValues, 4);
330 CheckItem(rgValues + 1, 3);
331
332 // Remove last 2 items to ensure we don't crash
333 MemRemoveFromArray(rgValues, 0, 2, cValues, sizeof(ArrayValue), FALSE);
334 cValues -= 2;
335 }
336 finally
337 {
338 ReleaseMem(rgValues);
339 DutilUninitialize();
340 }
341 }
342
343 [Fact]
344 void MemUtilSwapTest()
345 {
346 HRESULT hr = S_OK;
347 ArrayValue *rgValues = NULL;
348 DWORD cValues = 0;
349
350 DutilInitialize(&DutilTestTraceError);
351
352 try
353 {
354 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&rgValues), 10, sizeof(ArrayValue), 10);
355 NativeAssert::Succeeded(hr, "Failed to grow array size to 10");
356
357 cValues = 10;
358 for (DWORD i = 0; i < cValues; ++i)
359 {
360 SetItem(rgValues + i, i);
361 }
362
363 // Swap first two
364 MemArraySwapItems(rgValues, 0, 1, sizeof(ArrayValue));
365 --cValues;
366
367 CheckItem(rgValues, 1);
368 CheckItem(rgValues + 1, 0);
369 for (DWORD i = 2; i < cValues; ++i)
370 {
371 CheckItem(rgValues + i, i);
372 }
373
374 // Swap them back
375 MemArraySwapItems(rgValues, 0, 1, sizeof(ArrayValue));
376 --cValues;
377
378 for (DWORD i = 0; i < cValues; ++i)
379 {
380 CheckItem(rgValues + i, i);
381 }
382
383 // Swap first and last items (index 0 and 9)
384 MemArraySwapItems(rgValues, 0, 9, sizeof(ArrayValue));
385 --cValues;
386
387 CheckItem(rgValues, 9);
388 CheckItem(rgValues + 9, 0);
389 for (DWORD i = 1; i < cValues - 1; ++i)
390 {
391 CheckItem(rgValues + i, i);
392 }
393
394 // Swap index 1 and 8
395 MemArraySwapItems(rgValues, 1, 8, sizeof(ArrayValue));
396 --cValues;
397
398 CheckItem(rgValues, 9);
399 CheckItem(rgValues + 1, 8);
400 CheckItem(rgValues + 8, 1);
401 CheckItem(rgValues + 9, 0);
402 for (DWORD i = 2; i < cValues - 2; ++i)
403 {
404 CheckItem(rgValues + i, i);
405 }
406
407 // Swap index 2 and 7
408 MemArraySwapItems(rgValues, 2, 7, sizeof(ArrayValue));
409 --cValues;
410
411 CheckItem(rgValues, 9);
412 CheckItem(rgValues + 1, 8);
413 CheckItem(rgValues + 2, 7);
414 CheckItem(rgValues + 7, 2);
415 CheckItem(rgValues + 8, 1);
416 CheckItem(rgValues + 9, 0);
417 for (DWORD i = 3; i < cValues - 3; ++i)
418 {
419 CheckItem(rgValues + i, i);
420 }
421
422 // Swap index 0 and 1
423 MemArraySwapItems(rgValues, 0, 1, sizeof(ArrayValue));
424 --cValues;
425
426 CheckItem(rgValues, 8);
427 CheckItem(rgValues + 1, 9);
428 CheckItem(rgValues + 2, 7);
429 CheckItem(rgValues + 7, 2);
430 CheckItem(rgValues + 8, 1);
431 CheckItem(rgValues + 9, 0);
432 for (DWORD i = 3; i < cValues - 3; ++i)
433 {
434 CheckItem(rgValues + i, i);
435 }
436 }
437 finally
438 {
439 ReleaseMem(rgValues);
440 DutilUninitialize();
441 }
442 }
443
444 private:
445 void SetItem(ArrayValue *pValue, DWORD dwValue)
446 {
447 HRESULT hr = S_OK;
448 pValue->dwNum = dwValue;
449
450 hr = StrAllocFormatted(&pValue->sczString, L"%u", dwValue);
451 NativeAssert::Succeeded(hr, "Failed to allocate string");
452 }
453
454 void CheckItem(ArrayValue *pValue, DWORD dwValue)
455 {
456 HRESULT hr = S_OK;
457 LPWSTR sczTemp = NULL;
458
459 try
460 {
461 NativeAssert::Equal(dwValue, pValue->dwNum);
462
463 hr = StrAllocFormatted(&sczTemp, L"%u", dwValue);
464 NativeAssert::Succeeded(hr, "Failed to allocate temp string");
465
466 NativeAssert::StringEqual(sczTemp, pValue->sczString, TRUE);
467
468 if (pValue->pvNull1 || pValue->pvNull2)
469 {
470 hr = E_FAIL;
471 ExitOnFailure(hr, "One of the expected NULL values wasn't NULL!");
472 }
473 }
474 finally
475 {
476 ReleaseStr(sczTemp);
477 }
478
479 LExit:
480 return;
481 }
482
483 void CheckNullItem(ArrayValue *pValue)
484 {
485 HRESULT hr = S_OK;
486
487 NativeAssert::Equal<DWORD>(0, pValue->dwNum);
488
489 if (pValue->sczString)
490 {
491 hr = E_FAIL;
492 ExitOnFailure(hr, "Item found isn't NULL!");
493 }
494
495 if (pValue->pvNull1 || pValue->pvNull2)
496 {
497 hr = E_FAIL;
498 ExitOnFailure(hr, "One of the expected NULL values wasn't NULL!");
499 }
500
501 LExit:
502 return;
503 }
504 };
505}
diff --git a/src/libs/dutil/test/DUtilUnitTest/MonUtilTest.cpp b/src/libs/dutil/test/DUtilUnitTest/MonUtilTest.cpp
new file mode 100644
index 00000000..273f2eb6
--- /dev/null
+++ b/src/libs/dutil/test/DUtilUnitTest/MonUtilTest.cpp
@@ -0,0 +1,487 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4#undef RemoveDirectory
5
6using namespace System;
7using namespace System::Collections::Generic;
8using namespace System::Runtime::InteropServices;
9using namespace Xunit;
10using namespace WixBuildTools::TestSupport;
11
12namespace DutilTests
13{
14 const int PREWAIT = 20;
15 const int POSTWAIT = 480;
16 const int FULLWAIT = 500;
17 const int SILENCEPERIOD = 100;
18
19 struct RegKey
20 {
21 HRESULT hr;
22 HKEY hkRoot;
23 LPCWSTR wzSubKey;
24 REG_KEY_BITNESS kbKeyBitness;
25 BOOL fRecursive;
26 };
27 struct Directory
28 {
29 HRESULT hr;
30 LPCWSTR wzPath;
31 BOOL fRecursive;
32 };
33 struct Results
34 {
35 RegKey *rgRegKeys;
36 DWORD cRegKeys;
37 Directory *rgDirectories;
38 DWORD cDirectories;
39 };
40
41 public delegate void MonGeneralDelegate(HRESULT, LPVOID);
42
43 public delegate void MonDriveStatusDelegate(WCHAR, BOOL, LPVOID);
44
45 public delegate void MonDirectoryDelegate(HRESULT, LPCWSTR, BOOL, LPVOID, LPVOID);
46
47 public delegate void MonRegKeyDelegate(HRESULT, HKEY, LPCWSTR, REG_KEY_BITNESS, BOOL, LPVOID, LPVOID);
48
49 static void MonGeneral(
50 __in HRESULT /*hrResult*/,
51 __in_opt LPVOID /*pvContext*/
52 )
53 {
54 Assert::True(false);
55 }
56
57 static void MonDriveStatus(
58 __in WCHAR /*chDrive*/,
59 __in BOOL /*fArriving*/,
60 __in_opt LPVOID /*pvContext*/
61 )
62 {
63 }
64
65 static void MonDirectory(
66 __in HRESULT hrResult,
67 __in_z LPCWSTR wzPath,
68 __in_z BOOL fRecursive,
69 __in_opt LPVOID pvContext,
70 __in_opt LPVOID pvDirectoryContext
71 )
72 {
73 Assert::Equal(S_OK, hrResult);
74 Assert::Equal<DWORD_PTR>(0, reinterpret_cast<DWORD_PTR>(pvDirectoryContext));
75
76 HRESULT hr = S_OK;
77 Results *pResults = reinterpret_cast<Results *>(pvContext);
78
79 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pResults->rgDirectories), pResults->cDirectories + 1, sizeof(Directory), 5);
80 NativeAssert::ValidReturnCode(hr, S_OK);
81 ++pResults->cDirectories;
82
83 pResults->rgDirectories[pResults->cDirectories - 1].hr = hrResult;
84 pResults->rgDirectories[pResults->cDirectories - 1].wzPath = wzPath;
85 pResults->rgDirectories[pResults->cDirectories - 1].fRecursive = fRecursive;
86 }
87
88 static void MonRegKey(
89 __in HRESULT hrResult,
90 __in HKEY hkRoot,
91 __in_z LPCWSTR wzSubKey,
92 __in REG_KEY_BITNESS kbKeyBitness,
93 __in_z BOOL fRecursive,
94 __in_opt LPVOID pvContext,
95 __in_opt LPVOID pvRegKeyContext
96 )
97 {
98 Assert::Equal<HRESULT>(S_OK, hrResult);
99 Assert::Equal<DWORD_PTR>(0, reinterpret_cast<DWORD_PTR>(pvRegKeyContext));
100
101 HRESULT hr = S_OK;
102 Results *pResults = reinterpret_cast<Results *>(pvContext);
103
104 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pResults->rgRegKeys), pResults->cRegKeys + 1, sizeof(RegKey), 5);
105 NativeAssert::ValidReturnCode(hr, S_OK);
106 ++pResults->cRegKeys;
107
108 pResults->rgRegKeys[pResults->cRegKeys - 1].hr = hrResult;
109 pResults->rgRegKeys[pResults->cRegKeys - 1].hkRoot = hkRoot;
110 pResults->rgRegKeys[pResults->cRegKeys - 1].wzSubKey = wzSubKey;
111 pResults->rgRegKeys[pResults->cRegKeys - 1].kbKeyBitness = kbKeyBitness;
112 pResults->rgRegKeys[pResults->cRegKeys - 1].fRecursive = fRecursive;
113 }
114
115 public ref class MonUtil
116 {
117 public:
118 void ClearResults(Results *pResults)
119 {
120 ReleaseNullMem(pResults->rgDirectories);
121 pResults->cDirectories = 0;
122 ReleaseNullMem(pResults->rgRegKeys);
123 pResults->cRegKeys = 0;
124 }
125
126 void RemoveDirectory(LPCWSTR wzPath)
127 {
128 DWORD dwRetryCount = 0;
129 const DWORD c_dwMaxRetryCount = 100;
130 const DWORD c_dwRetryInterval = 50;
131
132 HRESULT hr = DirEnsureDelete(wzPath, TRUE, TRUE);
133
134 // Monitoring a directory opens a handle to that directory, which means delete requests for that directory will succeed
135 // (and deletion will be "pending" until our monitor handle is closed)
136 // but deletion of the directory containing that directory cannot complete until the handle is closed. This means DirEnsureDelete()
137 // can sometimes encounter HRESULT_FROM_WIN32(ERROR_DIR_NOT_EMPTY) failures, which just means it needs to retry a bit later
138 // (after the waiter thread wakes up, it will release the handle)
139 while (HRESULT_FROM_WIN32(ERROR_DIR_NOT_EMPTY) == hr && c_dwMaxRetryCount > dwRetryCount)
140 {
141 ::Sleep(c_dwRetryInterval);
142 ++dwRetryCount;
143 hr = DirEnsureDelete(wzPath, TRUE, TRUE);
144 }
145
146 NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE, E_PATHNOTFOUND);
147 }
148
149 void TestDirectory(MON_HANDLE handle, Results *pResults)
150 {
151 HRESULT hr = S_OK;
152 LPWSTR sczShallowPath = NULL;
153 LPWSTR sczParentPath = NULL;
154 LPWSTR sczDeepPath = NULL;
155 LPWSTR sczChildPath = NULL;
156 LPWSTR sczChildFilePath = NULL;
157
158 try
159 {
160 hr = PathExpand(&sczShallowPath, L"%TEMP%\\MonUtilTest\\", PATH_EXPAND_ENVIRONMENT);
161 NativeAssert::ValidReturnCode(hr, S_OK);
162
163 hr = PathExpand(&sczParentPath, L"%TEMP%\\MonUtilTest\\sub\\folder\\that\\might\\not\\", PATH_EXPAND_ENVIRONMENT);
164 NativeAssert::ValidReturnCode(hr, S_OK);
165
166 hr = PathExpand(&sczDeepPath, L"%TEMP%\\MonUtilTest\\sub\\folder\\that\\might\\not\\exist\\", PATH_EXPAND_ENVIRONMENT);
167 NativeAssert::ValidReturnCode(hr, S_OK);
168
169 hr = PathExpand(&sczChildPath, L"%TEMP%\\MonUtilTest\\sub\\folder\\that\\might\\not\\exist\\some\\sub\\folder\\", PATH_EXPAND_ENVIRONMENT);
170 NativeAssert::ValidReturnCode(hr, S_OK);
171
172 hr = PathExpand(&sczChildFilePath, L"%TEMP%\\MonUtilTest\\sub\\folder\\that\\might\\not\\exist\\some\\sub\\folder\\file.txt", PATH_EXPAND_ENVIRONMENT);
173 NativeAssert::ValidReturnCode(hr, S_OK);
174
175 RemoveDirectory(sczShallowPath);
176
177 hr = MonAddDirectory(handle, sczDeepPath, TRUE, SILENCEPERIOD, NULL);
178 NativeAssert::ValidReturnCode(hr, S_OK);
179
180 hr = DirEnsureExists(sczParentPath, NULL);
181 NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE);
182 // Make sure creating the parent directory does nothing, even after silence period
183 ::Sleep(FULLWAIT);
184 Assert::Equal<DWORD>(0, pResults->cDirectories);
185
186 // Now create the target path, no notification until after the silence period
187 hr = DirEnsureExists(sczDeepPath, NULL);
188 NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE);
189 ::Sleep(PREWAIT);
190 Assert::Equal<DWORD>(0, pResults->cDirectories);
191
192 // Now after the full silence period, it should have triggered
193 ::Sleep(POSTWAIT);
194 Assert::Equal<DWORD>(1, pResults->cDirectories);
195 NativeAssert::ValidReturnCode(pResults->rgDirectories[0].hr, S_OK);
196
197 // Now delete the directory, along with a ton of parents. This verifies MonUtil will keep watching the closest parent that still exists.
198 RemoveDirectory(sczShallowPath);
199
200 ::Sleep(FULLWAIT);
201 Assert::Equal<DWORD>(2, pResults->cDirectories);
202 NativeAssert::ValidReturnCode(pResults->rgDirectories[1].hr, S_OK);
203
204 // Create the parent directory again, still should be nothing even after full silence period
205 hr = DirEnsureExists(sczParentPath, NULL);
206 NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE);
207 ::Sleep(FULLWAIT);
208 Assert::Equal<DWORD>(2, pResults->cDirectories);
209
210 hr = DirEnsureExists(sczChildPath, NULL);
211 NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE);
212 ::Sleep(PREWAIT);
213 Assert::Equal<DWORD>(2, pResults->cDirectories);
214
215 ::Sleep(POSTWAIT);
216 Assert::Equal<DWORD>(3, pResults->cDirectories);
217 NativeAssert::ValidReturnCode(pResults->rgDirectories[2].hr, S_OK);
218
219 // Write a file to a deep child subfolder, and make sure it's detected
220 hr = FileFromString(sczChildFilePath, 0, L"contents", FILE_ENCODING_UTF16_WITH_BOM);
221 NativeAssert::ValidReturnCode(hr, S_OK);
222 ::Sleep(PREWAIT);
223 Assert::Equal<DWORD>(3, pResults->cDirectories);
224
225 ::Sleep(POSTWAIT);
226 Assert::Equal<DWORD>(4, pResults->cDirectories);
227 NativeAssert::ValidReturnCode(pResults->rgDirectories[2].hr, S_OK);
228
229 RemoveDirectory(sczParentPath);
230
231 ::Sleep(FULLWAIT);
232 Assert::Equal<DWORD>(5, pResults->cDirectories);
233 NativeAssert::ValidReturnCode(pResults->rgDirectories[3].hr, S_OK);
234
235 // Now remove the directory from the list of things to monitor, and confirm changes are no longer tracked
236 hr = MonRemoveDirectory(handle, sczDeepPath, TRUE);
237 NativeAssert::ValidReturnCode(hr, S_OK);
238 ::Sleep(PREWAIT);
239
240 hr = DirEnsureExists(sczDeepPath, NULL);
241 NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE);
242 ::Sleep(FULLWAIT);
243 Assert::Equal<DWORD>(5, pResults->cDirectories);
244 NativeAssert::ValidReturnCode(pResults->rgDirectories[3].hr, S_OK);
245
246 // Finally, add it back so we can test multiple things to monitor at once
247 hr = MonAddDirectory(handle, sczDeepPath, TRUE, SILENCEPERIOD, NULL);
248 NativeAssert::ValidReturnCode(hr, S_OK);
249 }
250 finally
251 {
252 ReleaseStr(sczShallowPath);
253 ReleaseStr(sczDeepPath);
254 ReleaseStr(sczParentPath);
255 }
256 }
257
258 void TestRegKey(MON_HANDLE handle, Results *pResults)
259 {
260 HRESULT hr = S_OK;
261 LPCWSTR wzShallowRegKey = L"Software\\MonUtilTest\\";
262 LPCWSTR wzParentRegKey = L"Software\\MonUtilTest\\sub\\folder\\that\\might\\not\\";
263 LPCWSTR wzDeepRegKey = L"Software\\MonUtilTest\\sub\\folder\\that\\might\\not\\exist\\";
264 LPCWSTR wzChildRegKey = L"Software\\MonUtilTest\\sub\\folder\\that\\might\\not\\exist\\some\\sub\\folder\\";
265 HKEY hk = NULL;
266
267 try
268 {
269 hr = RegDelete(HKEY_CURRENT_USER, wzShallowRegKey, REG_KEY_32BIT, TRUE);
270 NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE, E_PATHNOTFOUND);
271
272 hr = MonAddRegKey(handle, HKEY_CURRENT_USER, wzDeepRegKey, REG_KEY_DEFAULT, TRUE, SILENCEPERIOD, NULL);
273 NativeAssert::ValidReturnCode(hr, S_OK);
274
275 hr = RegCreate(HKEY_CURRENT_USER, wzParentRegKey, KEY_SET_VALUE | KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hk);
276 ReleaseRegKey(hk);
277 // Make sure creating the parent key does nothing, even after silence period
278 ::Sleep(FULLWAIT);
279 NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE);
280 Assert::Equal<DWORD>(0, pResults->cRegKeys);
281
282 // Now create the target path, no notification until after the silence period
283 hr = RegCreate(HKEY_CURRENT_USER, wzDeepRegKey, KEY_SET_VALUE | KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hk);
284 NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE);
285 ReleaseRegKey(hk);
286 ::Sleep(PREWAIT);
287 Assert::Equal<DWORD>(0, pResults->cRegKeys);
288
289 // Now after the full silence period, it should have triggered
290 ::Sleep(POSTWAIT);
291 Assert::Equal<DWORD>(1, pResults->cRegKeys);
292 NativeAssert::ValidReturnCode(pResults->rgRegKeys[0].hr, S_OK);
293
294 // Now delete the directory, along with a ton of parents. This verifies MonUtil will keep watching the closest parent that still exists.
295 hr = RegDelete(HKEY_CURRENT_USER, wzShallowRegKey, REG_KEY_32BIT, TRUE);
296 NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE, E_PATHNOTFOUND);
297 ::Sleep(PREWAIT);
298 Assert::Equal<DWORD>(1, pResults->cRegKeys);
299
300 ::Sleep(FULLWAIT);
301 Assert::Equal<DWORD>(2, pResults->cRegKeys);
302 NativeAssert::ValidReturnCode(pResults->rgRegKeys[1].hr, S_OK);
303
304 // Create the parent directory again, still should be nothing even after full silence period
305 hr = RegCreate(HKEY_CURRENT_USER, wzParentRegKey, KEY_SET_VALUE | KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hk);
306 NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE);
307 ReleaseRegKey(hk);
308 ::Sleep(FULLWAIT);
309 Assert::Equal<DWORD>(2, pResults->cRegKeys);
310
311 hr = RegCreate(HKEY_CURRENT_USER, wzChildRegKey, KEY_SET_VALUE | KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hk);
312 NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE);
313 ::Sleep(PREWAIT);
314 Assert::Equal<DWORD>(2, pResults->cRegKeys);
315
316 ::Sleep(FULLWAIT);
317 Assert::Equal<DWORD>(3, pResults->cRegKeys);
318 NativeAssert::ValidReturnCode(pResults->rgRegKeys[2].hr, S_OK);
319
320 // Write a registry value to some deep child subkey, and make sure it's detected
321 hr = RegWriteString(hk, L"valuename", L"testvalue");
322 NativeAssert::ValidReturnCode(hr, S_OK);
323 ReleaseRegKey(hk);
324 ::Sleep(PREWAIT);
325 Assert::Equal<DWORD>(3, pResults->cRegKeys);
326
327 ::Sleep(FULLWAIT);
328 Assert::Equal<DWORD>(4, pResults->cRegKeys);
329 NativeAssert::ValidReturnCode(pResults->rgRegKeys[2].hr, S_OK);
330
331 hr = RegDelete(HKEY_CURRENT_USER, wzDeepRegKey, REG_KEY_32BIT, TRUE);
332 NativeAssert::ValidReturnCode(hr, S_OK);
333
334 ::Sleep(FULLWAIT);
335 Assert::Equal<DWORD>(5, pResults->cRegKeys);
336
337 // Now remove the regkey from the list of things to monitor, and confirm changes are no longer tracked
338 hr = MonRemoveRegKey(handle, HKEY_CURRENT_USER, wzDeepRegKey, REG_KEY_DEFAULT, TRUE);
339 NativeAssert::ValidReturnCode(hr, S_OK);
340
341 hr = RegCreate(HKEY_CURRENT_USER, wzDeepRegKey, KEY_SET_VALUE | KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hk);
342 NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE);
343 ReleaseRegKey(hk);
344 ::Sleep(FULLWAIT);
345 Assert::Equal<DWORD>(5, pResults->cRegKeys);
346 }
347 finally
348 {
349 ReleaseRegKey(hk);
350 }
351 }
352
353 void TestMoreThan64(MON_HANDLE handle, Results *pResults)
354 {
355 HRESULT hr = S_OK;
356 LPWSTR sczBaseDir = NULL;
357 LPWSTR sczDir = NULL;
358 LPWSTR sczFile = NULL;
359
360 try
361 {
362 hr = PathExpand(&sczBaseDir, L"%TEMP%\\ScalabilityTest\\", PATH_EXPAND_ENVIRONMENT);
363 NativeAssert::ValidReturnCode(hr, S_OK);
364
365 for (DWORD i = 0; i < 200; ++i)
366 {
367 hr = StrAllocFormatted(&sczDir, L"%ls%u\\", sczBaseDir, i);
368 NativeAssert::ValidReturnCode(hr, S_OK);
369
370 hr = DirEnsureExists(sczDir, NULL);
371 NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE);
372
373 hr = MonAddDirectory(handle, sczDir, FALSE, SILENCEPERIOD, NULL);
374 NativeAssert::ValidReturnCode(hr, S_OK);
375 }
376
377 hr = PathConcat(sczDir, L"file.txt", &sczFile);
378 NativeAssert::ValidReturnCode(hr, S_OK);
379
380 hr = FileFromString(sczFile, 0, L"contents", FILE_ENCODING_UTF16_WITH_BOM);
381 NativeAssert::ValidReturnCode(hr, S_OK);
382
383 ::Sleep(FULLWAIT);
384 Assert::Equal<DWORD>(1, pResults->cDirectories);
385
386 for (DWORD i = 0; i < 199; ++i)
387 {
388 hr = StrAllocFormatted(&sczDir, L"%ls%u\\", sczBaseDir, i);
389 NativeAssert::ValidReturnCode(hr, S_OK);
390
391 hr = MonRemoveDirectory(handle, sczDir, FALSE);
392 NativeAssert::ValidReturnCode(hr, S_OK);
393 }
394 ::Sleep(FULLWAIT);
395
396 hr = FileFromString(sczFile, 0, L"contents2", FILE_ENCODING_UTF16_WITH_BOM);
397 NativeAssert::ValidReturnCode(hr, S_OK);
398
399 ::Sleep(FULLWAIT);
400 Assert::Equal<DWORD>(2, pResults->cDirectories);
401
402 for (DWORD i = 0; i < 199; ++i)
403 {
404 hr = StrAllocFormatted(&sczDir, L"%ls%u\\", sczBaseDir, i);
405 NativeAssert::ValidReturnCode(hr, S_OK);
406
407 hr = MonAddDirectory(handle, sczDir, FALSE, SILENCEPERIOD, NULL);
408 NativeAssert::ValidReturnCode(hr, S_OK);
409 }
410 ::Sleep(FULLWAIT);
411
412 hr = FileFromString(sczFile, 0, L"contents3", FILE_ENCODING_UTF16_WITH_BOM);
413 NativeAssert::ValidReturnCode(hr, S_OK);
414
415 ::Sleep(FULLWAIT);
416 Assert::Equal<DWORD>(3, pResults->cDirectories);
417 }
418 finally
419 {
420 ReleaseStr(sczBaseDir);
421 ReleaseStr(sczDir);
422 ReleaseStr(sczFile);
423 }
424 }
425
426 [Fact(Skip = "Test demonstrates failure")]
427 void MonUtilTest()
428 {
429 HRESULT hr = S_OK;
430 MON_HANDLE handle = NULL;
431 List<GCHandle>^ gcHandles = gcnew List<GCHandle>();
432 Results *pResults = (Results *)MemAlloc(sizeof(Results), TRUE);
433 Assert::True(NULL != pResults);
434
435 try
436 {
437 // These ensure the function pointers we send point to this thread's appdomain, which helps with assembly binding when running tests within msbuild
438 MonGeneralDelegate^ fpMonGeneral = gcnew MonGeneralDelegate(MonGeneral);
439 GCHandle gchMonGeneral = GCHandle::Alloc(fpMonGeneral);
440 gcHandles->Add(gchMonGeneral);
441 IntPtr ipMonGeneral = Marshal::GetFunctionPointerForDelegate(fpMonGeneral);
442
443 MonDriveStatusDelegate^ fpMonDriveStatus = gcnew MonDriveStatusDelegate(MonDriveStatus);
444 GCHandle gchMonDriveStatus = GCHandle::Alloc(fpMonDriveStatus);
445 gcHandles->Add(gchMonDriveStatus);
446 IntPtr ipMonDriveStatus = Marshal::GetFunctionPointerForDelegate(fpMonDriveStatus);
447
448 MonDirectoryDelegate^ fpMonDirectory = gcnew MonDirectoryDelegate(MonDirectory);
449 GCHandle gchMonDirectory = GCHandle::Alloc(fpMonDirectory);
450 gcHandles->Add(gchMonDirectory);
451 IntPtr ipMonDirectory = Marshal::GetFunctionPointerForDelegate(fpMonDirectory);
452
453 MonRegKeyDelegate^ fpMonRegKey = gcnew MonRegKeyDelegate(MonRegKey);
454 GCHandle gchMonRegKey = GCHandle::Alloc(fpMonRegKey);
455 gcHandles->Add(gchMonRegKey);
456 IntPtr ipMonRegKey = Marshal::GetFunctionPointerForDelegate(fpMonRegKey);
457
458 // "Silence period" is 100 ms
459 hr = MonCreate(&handle, static_cast<PFN_MONGENERAL>(ipMonGeneral.ToPointer()), static_cast<PFN_MONDRIVESTATUS>(ipMonDriveStatus.ToPointer()), static_cast<PFN_MONDIRECTORY>(ipMonDirectory.ToPointer()), static_cast<PFN_MONREGKEY>(ipMonRegKey.ToPointer()), pResults);
460 NativeAssert::ValidReturnCode(hr, S_OK);
461
462 hr = RegInitialize();
463 NativeAssert::ValidReturnCode(hr, S_OK);
464
465 TestDirectory(handle, pResults);
466 ClearResults(pResults);
467 TestRegKey(handle, pResults);
468 ClearResults(pResults);
469 TestMoreThan64(handle, pResults);
470 ClearResults(pResults);
471 }
472 finally
473 {
474 ReleaseMon(handle);
475
476 for each (GCHandle gcHandle in gcHandles)
477 {
478 gcHandle.Free();
479 }
480
481 ReleaseMem(pResults->rgDirectories);
482 ReleaseMem(pResults->rgRegKeys);
483 ReleaseMem(pResults);
484 }
485 }
486 };
487}
diff --git a/src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp b/src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp
new file mode 100644
index 00000000..5a1f06fd
--- /dev/null
+++ b/src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp
@@ -0,0 +1,80 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5using namespace System;
6using namespace Xunit;
7using namespace WixBuildTools::TestSupport;
8
9namespace DutilTests
10{
11 public ref class PathUtil
12 {
13 public:
14 [Fact]
15 void PathGetHierarchyArrayTest()
16 {
17 HRESULT hr = S_OK;
18 LPWSTR *rgsczPaths = NULL;
19 UINT cPaths = 0;
20
21 try
22 {
23 hr = PathGetHierarchyArray(L"c:\\foo\\bar\\bas\\a.txt", &rgsczPaths, &cPaths);
24 NativeAssert::Succeeded(hr, "Failed to get parent directories array for regular file path");
25 Assert::Equal<DWORD>(5, cPaths);
26 NativeAssert::StringEqual(L"c:\\", rgsczPaths[0]);
27 NativeAssert::StringEqual(L"c:\\foo\\", rgsczPaths[1]);
28 NativeAssert::StringEqual(L"c:\\foo\\bar\\", rgsczPaths[2]);
29 NativeAssert::StringEqual(L"c:\\foo\\bar\\bas\\", rgsczPaths[3]);
30 NativeAssert::StringEqual(L"c:\\foo\\bar\\bas\\a.txt", rgsczPaths[4]);
31 ReleaseNullStrArray(rgsczPaths, cPaths);
32
33 hr = PathGetHierarchyArray(L"c:\\foo\\bar\\bas\\", &rgsczPaths, &cPaths);
34 NativeAssert::Succeeded(hr, "Failed to get parent directories array for regular directory path");
35 Assert::Equal<DWORD>(4, cPaths);
36 NativeAssert::StringEqual(L"c:\\", rgsczPaths[0]);
37 NativeAssert::StringEqual(L"c:\\foo\\", rgsczPaths[1]);
38 NativeAssert::StringEqual(L"c:\\foo\\bar\\", rgsczPaths[2]);
39 NativeAssert::StringEqual(L"c:\\foo\\bar\\bas\\", rgsczPaths[3]);
40 ReleaseNullStrArray(rgsczPaths, cPaths);
41
42 hr = PathGetHierarchyArray(L"\\\\server\\share\\subdir\\file.txt", &rgsczPaths, &cPaths);
43 NativeAssert::Succeeded(hr, "Failed to get parent directories array for UNC file path");
44 Assert::Equal<DWORD>(3, cPaths);
45 NativeAssert::StringEqual(L"\\\\server\\share\\", rgsczPaths[0]);
46 NativeAssert::StringEqual(L"\\\\server\\share\\subdir\\", rgsczPaths[1]);
47 NativeAssert::StringEqual(L"\\\\server\\share\\subdir\\file.txt", rgsczPaths[2]);
48 ReleaseNullStrArray(rgsczPaths, cPaths);
49
50 hr = PathGetHierarchyArray(L"\\\\server\\share\\subdir\\", &rgsczPaths, &cPaths);
51 NativeAssert::Succeeded(hr, "Failed to get parent directories array for UNC directory path");
52 Assert::Equal<DWORD>(2, cPaths);
53 NativeAssert::StringEqual(L"\\\\server\\share\\", rgsczPaths[0]);
54 NativeAssert::StringEqual(L"\\\\server\\share\\subdir\\", rgsczPaths[1]);
55 ReleaseNullStrArray(rgsczPaths, cPaths);
56
57 hr = PathGetHierarchyArray(L"Software\\Microsoft\\Windows\\ValueName", &rgsczPaths, &cPaths);
58 NativeAssert::Succeeded(hr, "Failed to get parent directories array for UNC directory path");
59 Assert::Equal<DWORD>(4, cPaths);
60 NativeAssert::StringEqual(L"Software\\", rgsczPaths[0]);
61 NativeAssert::StringEqual(L"Software\\Microsoft\\", rgsczPaths[1]);
62 NativeAssert::StringEqual(L"Software\\Microsoft\\Windows\\", rgsczPaths[2]);
63 NativeAssert::StringEqual(L"Software\\Microsoft\\Windows\\ValueName", rgsczPaths[3]);
64 ReleaseNullStrArray(rgsczPaths, cPaths);
65
66 hr = PathGetHierarchyArray(L"Software\\Microsoft\\Windows\\", &rgsczPaths, &cPaths);
67 NativeAssert::Succeeded(hr, "Failed to get parent directories array for UNC directory path");
68 Assert::Equal<DWORD>(3, cPaths);
69 NativeAssert::StringEqual(L"Software\\", rgsczPaths[0]);
70 NativeAssert::StringEqual(L"Software\\Microsoft\\", rgsczPaths[1]);
71 NativeAssert::StringEqual(L"Software\\Microsoft\\Windows\\", rgsczPaths[2]);
72 ReleaseNullStrArray(rgsczPaths, cPaths);
73 }
74 finally
75 {
76 ReleaseStrArray(rgsczPaths, cPaths);
77 }
78 }
79 };
80}
diff --git a/src/libs/dutil/test/DUtilUnitTest/SceUtilTest.cpp b/src/libs/dutil/test/DUtilUnitTest/SceUtilTest.cpp
new file mode 100644
index 00000000..75b9222a
--- /dev/null
+++ b/src/libs/dutil/test/DUtilUnitTest/SceUtilTest.cpp
@@ -0,0 +1,488 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5#include <sqlce_oledb.h>
6#include <sceutil.h>
7
8using namespace System;
9using namespace Xunit;
10using namespace WixTest;
11
12#define ASSIGN_INDEX_STRUCT(a, b, c) {a.wzName = c; a.rgColumns = b; a.cColumns = countof(b);};
13
14namespace DutilTests
15{
16 enum TABLES
17 {
18 TABLE_A,
19 TABLE_COUNT
20 };
21
22 enum TABLE_A_COLUMNS
23 {
24 TABLE_A_KEY,
25 TABLE_A_BINARY,
26 TABLE_A_DWORD,
27 TABLE_A_QWORD,
28 TABLE_A_BOOL,
29 TABLE_A_STRING,
30 TABLE_A_DWORD_NULLABLE,
31 TABLE_A_INITIAL_COLUMNS,
32
33 TABLE_A_EXTRA_STRING = TABLE_A_INITIAL_COLUMNS,
34 TABLE_A_FINAL_COLUMNS
35 };
36
37 struct TableARowValue
38 {
39 DWORD dwAutoGenKey;
40
41 BYTE *pbBinary;
42 DWORD cBinary;
43
44 DWORD dw;
45 DWORD64 qw;
46 BOOL f;
47 LPWSTR scz;
48
49 BOOL fNullablePresent;
50 DWORD dwNullable;
51
52 BOOL fSchemaV2;
53 LPWSTR sczExtra;
54 };
55
56 public ref class SceUtil
57 {
58 public:
59 void ReleaseSceSchema(SCE_DATABASE_SCHEMA *pdsSchema)
60 {
61 DWORD dwTable;
62
63 for (dwTable = 0; dwTable < pdsSchema->cTables; ++dwTable)
64 {
65 ReleaseNullMem(pdsSchema->rgTables[dwTable].rgColumns);
66 ReleaseNullMem(pdsSchema->rgTables[dwTable].rgIndexes);
67 }
68
69 ReleaseMem(pdsSchema->rgTables);
70
71 return;
72 }
73
74 void SetupSchema(SCE_DATABASE_SCHEMA *pSchema, BOOL fIncludeExtended)
75 {
76 pSchema->cTables = TABLE_COUNT;
77 pSchema->rgTables = static_cast<SCE_TABLE_SCHEMA*>(MemAlloc(TABLE_COUNT * sizeof(SCE_TABLE_SCHEMA), TRUE));
78 NativeAssert::True(pSchema->rgTables != NULL);
79
80 pSchema->rgTables[TABLE_A].wzName = L"TableA";
81 pSchema->rgTables[TABLE_A].cColumns = fIncludeExtended ? TABLE_A_FINAL_COLUMNS : TABLE_A_INITIAL_COLUMNS;
82 pSchema->rgTables[TABLE_A].cIndexes = 2;
83
84 for (DWORD i = 0; i < pSchema->cTables; ++i)
85 {
86 pSchema->rgTables[i].rgColumns = static_cast<SCE_COLUMN_SCHEMA*>(MemAlloc(sizeof(SCE_COLUMN_SCHEMA) * pSchema->rgTables[i].cColumns, TRUE));
87 NativeAssert::True(pSchema->rgTables[i].rgColumns != NULL);
88
89 pSchema->rgTables[i].rgIndexes = static_cast<SCE_INDEX_SCHEMA*>(MemAlloc(sizeof(SCE_COLUMN_SCHEMA) * pSchema->rgTables[i].cIndexes, TRUE));
90 NativeAssert::True(pSchema->rgTables[i].rgIndexes != NULL);
91 }
92
93 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_KEY].wzName = L"Key";
94 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_KEY].dbtColumnType = DBTYPE_I4;
95 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_KEY].fPrimaryKey = TRUE;
96 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_KEY].fAutoIncrement = TRUE;
97 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_BINARY].wzName = L"Binary";
98 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_BINARY].dbtColumnType = DBTYPE_BYTES;
99 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_DWORD].wzName = L"Dword";
100 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_DWORD].dbtColumnType = DBTYPE_I4;
101 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_QWORD].wzName = L"Qword";
102 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_QWORD].dbtColumnType = DBTYPE_I8;
103 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_BOOL].wzName = L"Bool";
104 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_BOOL].dbtColumnType = DBTYPE_BOOL;
105 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_STRING].wzName = L"String";
106 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_STRING].dbtColumnType = DBTYPE_WSTR;
107 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_DWORD_NULLABLE].wzName = L"Nullable";
108 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_DWORD_NULLABLE].dbtColumnType = DBTYPE_I4;
109 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_DWORD_NULLABLE].fNullable = TRUE;
110
111 if (fIncludeExtended)
112 {
113 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_EXTRA_STRING].wzName = L"ExtraString";
114 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_EXTRA_STRING].dbtColumnType = DBTYPE_WSTR;
115 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_EXTRA_STRING].fNullable = TRUE;
116 }
117
118 static DWORD rgdwTableA_Index1[] = { TABLE_A_DWORD, TABLE_A_STRING, TABLE_A_QWORD };
119 static DWORD rgdwTableA_Index2[] = { TABLE_A_DWORD, TABLE_A_STRING };
120
121 ASSIGN_INDEX_STRUCT(pSchema->rgTables[TABLE_A].rgIndexes[0], rgdwTableA_Index1, L"Dword_String_Qword");
122 ASSIGN_INDEX_STRUCT(pSchema->rgTables[TABLE_A].rgIndexes[1], rgdwTableA_Index2, L"Dword_String");
123 }
124
125 void SetStructValues(TableARowValue *pValue, BYTE *pbBinary, DWORD cBinary, DWORD dw, DWORD64 qw, BOOL f, LPWSTR scz, DWORD *pdw, LPWSTR sczExtra)
126 {
127 pValue->pbBinary = pbBinary;
128 pValue->cBinary = cBinary;
129 pValue->dw = dw;
130 pValue->qw = qw;
131 pValue->f = f;
132 pValue->scz = scz;
133
134 if (pdw)
135 {
136 pValue->fNullablePresent = TRUE;
137 pValue->dwNullable = *pdw;
138 }
139 else
140 {
141 pValue->fNullablePresent = FALSE;
142 }
143
144 if (sczExtra)
145 {
146 pValue->fSchemaV2 = TRUE;
147 pValue->sczExtra = sczExtra;
148 }
149 else
150 {
151 pValue->fSchemaV2 = FALSE;
152 }
153 }
154
155 void AssertStructValuesSame(TableARowValue *pValueExpected, TableARowValue *pValueOther)
156 {
157 NativeAssert::Equal(pValueExpected->cBinary, pValueOther->cBinary);
158 NativeAssert::True(0 == memcmp(pValueExpected->pbBinary, pValueOther->pbBinary, pValueOther->cBinary));
159
160 NativeAssert::Equal(pValueExpected->dw, pValueOther->dw);
161 NativeAssert::Equal(pValueExpected->qw, pValueOther->qw);
162 NativeAssert::Equal(pValueExpected->f, pValueOther->f);
163 NativeAssert::True(0 == wcscmp(pValueExpected->scz, pValueOther->scz));
164
165 NativeAssert::Equal(pValueExpected->fNullablePresent, pValueOther->fNullablePresent);
166 if (pValueExpected->fNullablePresent)
167 {
168 NativeAssert::Equal(pValueExpected->dwNullable, pValueOther->dwNullable);
169 }
170
171 NativeAssert::Equal(pValueExpected->fSchemaV2, pValueOther->fSchemaV2);
172 if (pValueExpected->fSchemaV2)
173 {
174 NativeAssert::True(0 == wcscmp(pValueExpected->sczExtra, pValueOther->sczExtra));
175 }
176 }
177
178 void InsertRow(SCE_DATABASE *pDatabase, TableARowValue *pValue, BOOL fRollback)
179 {
180 HRESULT hr = S_OK;
181 SCE_ROW_HANDLE sceRow = NULL;
182
183 hr = SceBeginTransaction(pDatabase);
184 NativeAssert::Succeeded(hr, "Failed to begin transaction");
185
186 hr = ScePrepareInsert(pDatabase, TABLE_A, &sceRow);
187 NativeAssert::Succeeded(hr, "Failed to prepare to insert row");
188
189 hr = SceSetColumnBinary(sceRow, TABLE_A_BINARY, pValue->pbBinary, pValue->cBinary);
190 NativeAssert::Succeeded(hr, "Failed to set binary value");
191
192 hr = SceSetColumnDword(sceRow, TABLE_A_DWORD, pValue->dw);
193 NativeAssert::Succeeded(hr, "Failed to set dword value");
194
195 hr = SceSetColumnQword(sceRow, TABLE_A_QWORD, pValue->qw);
196 NativeAssert::Succeeded(hr, "Failed to set qword value");
197
198 hr = SceSetColumnBool(sceRow, TABLE_A_BOOL, pValue->f);
199 NativeAssert::Succeeded(hr, "Failed to set bool value");
200
201 hr = SceSetColumnString(sceRow, TABLE_A_STRING, pValue->scz);
202 NativeAssert::Succeeded(hr, "Failed to set string value");
203
204 if (pValue->fNullablePresent)
205 {
206 hr = SceSetColumnDword(sceRow, TABLE_A_DWORD_NULLABLE, pValue->dwNullable);
207 NativeAssert::Succeeded(hr, "Failed to set dword value");
208 }
209 else
210 {
211 hr = SceSetColumnNull(sceRow, TABLE_A_DWORD_NULLABLE);
212 NativeAssert::Succeeded(hr, "Failed to set null value");
213 }
214
215 if (pValue->fSchemaV2)
216 {
217 hr = SceSetColumnString(sceRow, TABLE_A_EXTRA_STRING, pValue->sczExtra);
218 NativeAssert::Succeeded(hr, "Failed to set extra string value");
219 }
220
221 hr = SceFinishUpdate(sceRow);
222 NativeAssert::Succeeded(hr, "Failed to finish insert");
223
224 if (fRollback)
225 {
226 hr = SceRollbackTransaction(pDatabase);
227 NativeAssert::Succeeded(hr, "Failed to rollback transaction");
228 }
229 else
230 {
231 hr = SceCommitTransaction(pDatabase);
232 NativeAssert::Succeeded(hr, "Failed to commit transaction");
233
234 hr = SceGetColumnDword(sceRow, TABLE_A_KEY, &pValue->dwAutoGenKey);
235 NativeAssert::Succeeded(hr, "Failed to get autogen key after insert");
236
237 NativeAssert::True(pValue->dwAutoGenKey != 0);
238 }
239
240 ReleaseSceRow(sceRow);
241 }
242
243 void VerifyRow(TableARowValue *pExpectedValue, SCE_ROW_HANDLE sceRow)
244 {
245 HRESULT hr = S_OK;
246 TableARowValue value = {};
247
248 hr = SceGetColumnBinary(sceRow, TABLE_A_BINARY, &value.pbBinary, &value.cBinary);
249 NativeAssert::Succeeded(hr, "Failed to get binary value from result row");
250
251 hr = SceGetColumnDword(sceRow, TABLE_A_DWORD, &value.dw);
252 NativeAssert::Succeeded(hr, "Failed to get dword value from result row");
253
254 hr = SceGetColumnQword(sceRow, TABLE_A_QWORD, &value.qw);
255 NativeAssert::Succeeded(hr, "Failed to get qword value from result row");
256
257 hr = SceGetColumnBool(sceRow, TABLE_A_BOOL, &value.f);
258 NativeAssert::Succeeded(hr, "Failed to get bool value from result row");
259
260 hr = SceGetColumnString(sceRow, TABLE_A_STRING, &value.scz);
261 NativeAssert::Succeeded(hr, "Failed to get string value from result row");
262
263 hr = SceGetColumnDword(sceRow, TABLE_A_DWORD_NULLABLE, &value.dwNullable);
264 if (hr == E_NOTFOUND)
265 {
266 value.fNullablePresent = FALSE;
267 hr = S_OK;
268 }
269 else
270 {
271 NativeAssert::Succeeded(hr, "Failed to get string value from result row");
272 value.fNullablePresent = TRUE;
273 }
274
275 if (pExpectedValue->fSchemaV2)
276 {
277 value.fSchemaV2 = TRUE;
278 hr = SceGetColumnString(sceRow, TABLE_A_EXTRA_STRING, &value.sczExtra);
279 NativeAssert::Succeeded(hr, "Failed to get extra string value from result row");
280 }
281
282 AssertStructValuesSame(pExpectedValue, &value);
283
284 ReleaseNullMem(value.pbBinary);
285 ReleaseNullStr(value.scz);
286 }
287
288 void VerifyQuery(TableARowValue **rgExpectedValues, DWORD cExpectedValues, SCE_QUERY_RESULTS_HANDLE queryResults)
289 {
290 HRESULT hr = S_OK;
291 SCE_ROW_HANDLE sceRow = NULL;
292
293 for (DWORD i = 0; i < cExpectedValues; ++i)
294 {
295 hr = SceGetNextResultRow(queryResults, &sceRow);
296 NativeAssert::Succeeded(hr, "Failed to get next result row");
297
298 VerifyRow(rgExpectedValues[i], sceRow);
299 ReleaseNullSceRow(sceRow);
300 }
301
302 // No more results
303 NativeAssert::True(NULL == queryResults || FAILED(SceGetNextResultRow(queryResults, &sceRow)));
304 }
305
306 void TestIndex(SCE_DATABASE *pDatabase)
307 {
308 HRESULT hr = S_OK;
309 BYTE binary1[50] = { 0x80, 0x70 };
310 BYTE binary2[40] = { 0x90, 0xAB };
311 BYTE binary3[40] = { 0x85, 0x88 };
312 DWORD dwValue1 = 0x55555555, dwValue2 = 0x88888888;
313 TableARowValue value1 = {}, value2 = {}, value3 = {}, value4 = {}, value5 = {};
314 SCE_QUERY_HANDLE query = NULL;
315 SCE_QUERY_RESULTS_HANDLE results = NULL;
316
317 SetStructValues(&value1, static_cast<BYTE *>(binary1), sizeof(binary1), 3, 1, TRUE, L"zzz", &dwValue1, NULL);
318 SetStructValues(&value2, static_cast<BYTE *>(binary2), sizeof(binary2), 3, 2, TRUE, L"yyy", &dwValue2, NULL);
319 SetStructValues(&value3, static_cast<BYTE *>(binary3), sizeof(binary3), 3, 3, TRUE, L"xxx", NULL, NULL);
320 SetStructValues(&value4, static_cast<BYTE *>(binary2), sizeof(binary2), 4, 4, TRUE, L"xyz", &dwValue2, NULL);
321 SetStructValues(&value5, static_cast<BYTE *>(binary3), sizeof(binary3), 3, 1, TRUE, L"yyy", &dwValue2, NULL);
322
323 // Rollback an insert to confirm the insert doesn't happen and database can still be interacted with normally afterwards
324 InsertRow(pDatabase, &value1, TRUE);
325
326 InsertRow(pDatabase, &value1, FALSE);
327 InsertRow(pDatabase, &value2, FALSE);
328 InsertRow(pDatabase, &value3, FALSE);
329 InsertRow(pDatabase, &value4, FALSE);
330 InsertRow(pDatabase, &value5, FALSE);
331
332 NativeAssert::True(value1.dwAutoGenKey != value2.dwAutoGenKey);
333
334 // Test setting 1 column
335 hr = SceBeginQuery(pDatabase, TABLE_A, 0, &query);
336 NativeAssert::Succeeded(hr, "Failed to begin query");
337
338 hr = SceSetQueryColumnDword(query, 3);
339 NativeAssert::Succeeded(hr, "Failed to set query column dword");
340
341 hr = SceRunQueryRange(&query, &results);
342 NativeAssert::Succeeded(hr, "Failed to run query");
343 NativeAssert::True(query == NULL);
344
345 TableARowValue *sortedAfterQuery1[] = { &value3, &value5, &value2, &value1 };
346 VerifyQuery(sortedAfterQuery1, _countof(sortedAfterQuery1), results);
347 ReleaseNullSceQueryResults(results);
348
349 // Test setting 2 columns, third column is unspecified so results are sorted by it
350 hr = SceBeginQuery(pDatabase, TABLE_A, 0, &query);
351 NativeAssert::Succeeded(hr, "Failed to begin query");
352
353 hr = SceSetQueryColumnDword(query, 3);
354 NativeAssert::Succeeded(hr, "Failed to set query column dword");
355
356 hr = SceSetQueryColumnString(query, L"yyy");
357 NativeAssert::Succeeded(hr, "Failed to set query column dword");
358
359 hr = SceRunQueryRange(&query, &results);
360 NativeAssert::Succeeded(hr, "Failed to run query");
361 NativeAssert::True(query == NULL);
362
363 TableARowValue *sortedAfterQuery2[] = { &value5, &value2 };
364 VerifyQuery(sortedAfterQuery2, _countof(sortedAfterQuery2), results);
365 ReleaseNullSceQueryResults(results);
366
367 // Test setting 2 columns, third column of index is unspecified so results are sorted by it
368 hr = SceBeginQuery(pDatabase, TABLE_A, 0, &query);
369 NativeAssert::Succeeded(hr, "Failed to begin query");
370
371 hr = SceSetQueryColumnDword(query, 3);
372 NativeAssert::Succeeded(hr, "Failed to set query column dword");
373
374 hr = SceSetQueryColumnString(query, L"yyy");
375 NativeAssert::Succeeded(hr, "Failed to set query column dword");
376
377 hr = SceRunQueryRange(&query, &results);
378 NativeAssert::Succeeded(hr, "Failed to run query");
379 NativeAssert::True(query == NULL);
380
381 TableARowValue *sortedAfterQuery3[] = { &value5, &value2 };
382 VerifyQuery(sortedAfterQuery3, _countof(sortedAfterQuery3), results);
383 ReleaseNullSceQueryResults(results);
384
385 // Test setting 2 columns in a different (2 column) index, so there is no 3rd column in index to sort by
386 hr = SceBeginQuery(pDatabase, TABLE_A, 1, &query);
387 NativeAssert::Succeeded(hr, "Failed to begin query");
388
389 hr = SceSetQueryColumnDword(query, 3);
390 NativeAssert::Succeeded(hr, "Failed to set query column dword");
391
392 hr = SceSetQueryColumnString(query, L"yyy");
393 NativeAssert::Succeeded(hr, "Failed to set query column dword");
394
395 hr = SceRunQueryRange(&query, &results);
396 NativeAssert::Succeeded(hr, "Failed to run query");
397 NativeAssert::True(query == NULL);
398
399 TableARowValue *sortedAfterQuery4[] = { &value2, &value5 };
400 VerifyQuery(sortedAfterQuery4, _countof(sortedAfterQuery4), results);
401 ReleaseNullSceQueryResults(results);
402 }
403
404 void TestReadWriteSchemaV2(SCE_DATABASE *pDatabase)
405 {
406 HRESULT hr = S_OK;
407 BYTE binary1[40] = { 0x55, 0x44 };
408 DWORD dwValue1 = 58;
409 TableARowValue value1 = {};
410 SCE_QUERY_HANDLE query = NULL;
411 SCE_ROW_HANDLE row = NULL;
412
413 SetStructValues(&value1, static_cast<BYTE *>(binary1), sizeof(binary1), 5, 1, TRUE, L"zzz", &dwValue1, L"newextrastring");
414
415 InsertRow(pDatabase, &value1, FALSE);
416
417 // Test setting 1 column
418 hr = SceBeginQuery(pDatabase, TABLE_A, 0, &query);
419 NativeAssert::Succeeded(hr, "Failed to begin query");
420
421 hr = SceSetQueryColumnDword(query, 5);
422 NativeAssert::Succeeded(hr, "Failed to set query column dword");
423
424 hr = SceRunQueryExact(&query, &row);
425 NativeAssert::Succeeded(hr, "Failed to run query exact");
426
427 VerifyRow(&value1, row);
428 }
429
430 [Fact]
431 void SceUtilTest()
432 {
433 HRESULT hr = S_OK;
434 BOOL fComInitialized = FALSE;
435 LPWSTR sczDbPath = NULL;
436 SCE_DATABASE *pDatabase = NULL;
437 SCE_DATABASE_SCHEMA schema1 = {};
438 SCE_DATABASE_SCHEMA schema2 = {};
439
440 try
441 {
442 hr = ::CoInitialize(0);
443 NativeAssert::Succeeded(hr, "Failed to initialize COM");
444 fComInitialized = TRUE;
445
446 SetupSchema(&schema1, FALSE);
447 SetupSchema(&schema2, TRUE);
448
449 hr = PathExpand(&sczDbPath, L"%TEMP%\\SceUtilTest\\UnitTest.sdf", PATH_EXPAND_ENVIRONMENT);
450 NativeAssert::Succeeded(hr, "Failed to get path to test database");
451
452 FileEnsureDelete(sczDbPath);
453
454 hr = SceEnsureDatabase(sczDbPath, L"sqlceoledb40.dll", L"Test", 1, &schema1, &pDatabase);
455 NativeAssert::Succeeded(hr, "Failed to ensure database schema");
456
457 TestIndex(pDatabase);
458
459 hr = SceCloseDatabase(pDatabase);
460 pDatabase = NULL;
461 NativeAssert::Succeeded(hr, "Failed to close database");
462
463 // Add column to schema
464 hr = SceEnsureDatabase(sczDbPath, L"sqlceoledb40.dll", L"Test", 1, &schema2, &pDatabase);
465 NativeAssert::Succeeded(hr, "Failed to ensure database schema");
466
467 TestReadWriteSchemaV2(pDatabase);
468 }
469 finally
470 {
471 ReleaseSceSchema(&schema1);
472 ReleaseSceSchema(&schema2);
473
474 if (NULL != pDatabase)
475 {
476 hr = SceCloseDatabase(pDatabase);
477 NativeAssert::Succeeded(hr, "Failed to close database");
478 }
479 ReleaseStr(sczDbPath);
480
481 if (fComInitialized)
482 {
483 ::CoUninitialize();
484 }
485 }
486 }
487 };
488}
diff --git a/src/libs/dutil/test/DUtilUnitTest/StrUtilTest.cpp b/src/libs/dutil/test/DUtilUnitTest/StrUtilTest.cpp
new file mode 100644
index 00000000..94fee280
--- /dev/null
+++ b/src/libs/dutil/test/DUtilUnitTest/StrUtilTest.cpp
@@ -0,0 +1,192 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5using namespace System;
6using namespace Xunit;
7using namespace WixBuildTools::TestSupport;
8
9namespace DutilTests
10{
11 public ref class StrUtil
12 {
13 public:
14 [Fact]
15 void StrUtilFormattedTest()
16 {
17 HRESULT hr = S_OK;
18 LPWSTR sczText = NULL;
19
20 try
21 {
22 hr = StrAllocFormatted(&sczText, L"%hs - %ls - %u", "ansi string", L"unicode string", 1234);
23 NativeAssert::Succeeded(hr, "Failed to format string.");
24 NativeAssert::StringEqual(L"ansi string - unicode string - 1234", sczText);
25
26 ReleaseNullStr(sczText);
27
28 hr = StrAllocString(&sczText, L"repeat", 0);
29 NativeAssert::Succeeded(hr, "Failed to allocate string.");
30
31 hr = StrAllocFormatted(&sczText, L"%ls and %ls", sczText, sczText);
32 NativeAssert::Succeeded(hr, "Failed to format string unto itself.");
33 NativeAssert::StringEqual(L"repeat and repeat", sczText);
34 }
35 finally
36 {
37 ReleaseStr(sczText);
38 }
39 }
40
41 [Fact]
42 void StrUtilTrimTest()
43 {
44 TestTrim(L"", L"");
45 TestTrim(L"Blah", L"Blah");
46 TestTrim(L"\t\t\tBlah", L"Blah");
47 TestTrim(L"\t Blah ", L"Blah");
48 TestTrim(L"Blah ", L"Blah");
49 TestTrim(L"\t Spaces \t Between \t", L"Spaces \t Between");
50 TestTrim(L" \t\t\t ", L"");
51
52 TestTrimAnsi("", "");
53 TestTrimAnsi("Blah", "Blah");
54 TestTrimAnsi("\t\t\tBlah", "Blah");
55 TestTrimAnsi(" Blah ", "Blah");
56 TestTrimAnsi("Blah ", "Blah");
57 TestTrimAnsi("\t Spaces \t Between \t", "Spaces \t Between");
58 TestTrimAnsi(" \t\t\t ", "");
59 }
60
61 [Fact]
62 void StrUtilConvertTest()
63 {
64 char a[] = { 'a', 'b', 'C', 'd', '\0', '\0' };
65
66 TestStrAllocStringAnsi(a, 5, L"abCd");
67 TestStrAllocStringAnsi(a, 4, L"abCd");
68 TestStrAllocStringAnsi(a, 3, L"abC");
69 TestStrAllocStringAnsi(a, 2, L"ab");
70 TestStrAllocStringAnsi(a, 1, L"a");
71 TestStrAllocStringAnsi(a, 0, L"abCd");
72
73 wchar_t b[] = { L'a', L'b', L'C', L'd', L'\0', L'\0' };
74
75 TestStrAnsiAllocString(b, 5, "abCd");
76 TestStrAnsiAllocString(b, 4, "abCd");
77 TestStrAnsiAllocString(b, 3, "abC");
78 TestStrAnsiAllocString(b, 2, "ab");
79 TestStrAnsiAllocString(b, 1, "a");
80 TestStrAnsiAllocString(b, 0, "abCd");
81 }
82
83 private:
84 void TestTrim(LPCWSTR wzInput, LPCWSTR wzExpectedResult)
85 {
86 HRESULT hr = S_OK;
87 LPWSTR sczOutput = NULL;
88
89 DutilInitialize(&DutilTestTraceError);
90
91 try
92 {
93 hr = StrTrimWhitespace(&sczOutput, wzInput);
94 NativeAssert::Succeeded(hr, "Failed to trim whitespace from string: {0}", wzInput);
95
96 if (0 != wcscmp(wzExpectedResult, sczOutput))
97 {
98 hr = E_FAIL;
99 ExitOnFailure(hr, "Trimmed string \"%ls\", expected result \"%ls\", actual result \"%ls\"", wzInput, wzExpectedResult, sczOutput);
100 }
101 }
102 finally
103 {
104 ReleaseStr(sczOutput);
105 }
106
107 LExit:
108 DutilUninitialize();
109 }
110
111 void TestTrimAnsi(LPCSTR szInput, LPCSTR szExpectedResult)
112 {
113 HRESULT hr = S_OK;
114 LPSTR sczOutput = NULL;
115
116 DutilInitialize(&DutilTestTraceError);
117
118 try
119 {
120 hr = StrAnsiTrimWhitespace(&sczOutput, szInput);
121 NativeAssert::Succeeded(hr, "Failed to trim whitespace from string: \"{0}\"", szInput);
122
123 if (0 != strcmp(szExpectedResult, sczOutput))
124 {
125 hr = E_FAIL;
126 ExitOnFailure(hr, "Trimmed string \"%hs\", expected result \"%hs\", actual result \"%hs\"", szInput, szExpectedResult, sczOutput);
127 }
128 }
129 finally
130 {
131 ReleaseStr(sczOutput);
132 }
133
134 LExit:
135 DutilUninitialize();
136 }
137
138 void TestStrAllocStringAnsi(LPCSTR szSource, DWORD cchSource, LPCWSTR wzExpectedResult)
139 {
140 HRESULT hr = S_OK;
141 LPWSTR sczOutput = NULL;
142
143 DutilInitialize(&DutilTestTraceError);
144
145 try
146 {
147 hr = StrAllocStringAnsi(&sczOutput, szSource, cchSource, CP_UTF8);
148 NativeAssert::Succeeded(hr, "Failed to call StrAllocStringAnsi on string: \"{0}\"", szSource);
149
150 if (0 != wcscmp(sczOutput, wzExpectedResult))
151 {
152 hr = E_FAIL;
153 ExitOnFailure(hr, "String doesn't match, expected result \"%ls\", actual result \"%ls\"", wzExpectedResult, sczOutput);
154 }
155 }
156 finally
157 {
158 ReleaseStr(sczOutput);
159 }
160
161 LExit:
162 DutilUninitialize();
163 }
164
165 void TestStrAnsiAllocString(LPWSTR wzSource, DWORD cchSource, LPCSTR szExpectedResult)
166 {
167 HRESULT hr = S_OK;
168 LPSTR sczOutput = NULL;
169
170 DutilInitialize(&DutilTestTraceError);
171
172 try
173 {
174 hr = StrAnsiAllocString(&sczOutput, wzSource, cchSource, CP_UTF8);
175 NativeAssert::Succeeded(hr, "Failed to call StrAllocStringAnsi on string: \"{0}\"", wzSource);
176
177 if (0 != strcmp(sczOutput, szExpectedResult))
178 {
179 hr = E_FAIL;
180 ExitOnFailure(hr, "String doesn't match, expected result \"%hs\", actual result \"%hs\"", szExpectedResult, sczOutput);
181 }
182 }
183 finally
184 {
185 ReleaseStr(sczOutput);
186 }
187
188 LExit:
189 DutilUninitialize();
190 }
191 };
192}
diff --git a/src/libs/dutil/test/DUtilUnitTest/TestData/ApupUtilTests/FeedBv2.0.xml b/src/libs/dutil/test/DUtilUnitTest/TestData/ApupUtilTests/FeedBv2.0.xml
new file mode 100644
index 00000000..d9f961fe
--- /dev/null
+++ b/src/libs/dutil/test/DUtilUnitTest/TestData/ApupUtilTests/FeedBv2.0.xml
@@ -0,0 +1,68 @@
1<?xml version='1.0' ?>
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<feed xmlns="http://www.w3.org/2005/Atom" xmlns:as="http://appsyndication.org/2006/appsyn">
6 <title type="text">BundleB v2.0</title>
7 <subtitle type="text">Bundle Subtitle.</subtitle>
8 <as:application type="application/exe">1116353B-7C6E-4C29-BFA1-D4A972CD421D</as:application>
9 <updated>2014-07-14T12:39:00.000Z</updated>
10 <id>http://localhost:9999/wix4/BundleB/feed</id>
11 <link rel="self" type="application/atom+xml" href="http://localhost:9999/wix4/BundleB/feed"/>
12 <generator version="0.1">manual build</generator>
13 <entry>
14 <title>Bundle v2.0</title>
15 <id>v2.0</id>
16 <author>
17 <name>Bundle_Author</name>
18 <uri>http://mycompany.com/software</uri>
19 <email>Bundle_Author@mycompany.com</email>
20 </author>
21 <link rel="alternate" href="http://www.mycompany.com/content/view/software"/>
22 <link rel="enclosure" href="http://localhost:9999/wix4/BundleB/2.0/BundleB.exe" length="0" type="application/octet-stream"/>
23 <content type="html">
24 &lt;p&gt;Change list:&lt;/p&gt;&lt;ul&gt;
25 &lt;li&gt;Updated release.&lt;/li&gt;
26 &lt;/ul&gt;
27 </content>
28 <as:version>2.0.0.0</as:version>
29 <updated>2014-11-10T12:39:00.000Z</updated>
30 </entry>
31 <entry>
32 <title>Bundle v1.0</title>
33 <id>v1.0</id>
34 <author>
35 <name>Bundle_Author</name>
36 <uri>http://mycompany.com/software</uri>
37 <email>Bundle_Author@mycompany.com</email>
38 </author>
39 <link rel="alternate" href="http://www.mycompany.com/content/view/software"/>
40 <link rel="enclosure" href="http://localhost:9999/wix4/BundleB/1.0/BundleB.exe" length="0" type="application/octet-stream"/>
41 <content type="html">
42 &lt;p&gt;Change list:&lt;/p&gt;&lt;ul&gt;
43 &lt;li&gt;Initial release.&lt;/li&gt;
44 &lt;/ul&gt;
45 </content>
46 <as:upgrade version="1.0.0.0-preview" />
47 <as:version>1.0.0.0</as:version>
48 <updated>2014-11-09T12:39:00.000Z</updated>
49 </entry>
50 <entry>
51 <title>Bundle v1.0-preview</title>
52 <id>v1.0-preview</id>
53 <author>
54 <name>Bundle_Author</name>
55 <uri>http://mycompany.com/software</uri>
56 <email>Bundle_Author@mycompany.com</email>
57 </author>
58 <link rel="alternate" href="http://www.mycompany.com/content/view/software"/>
59 <link rel="enclosure" href="http://localhost:9999/wix4/BundleB/1.0-preview/BundleB.exe" length="10000" type="application/octet-stream"/>
60 <content type="html">
61 &lt;p&gt;Change list:&lt;/p&gt;&lt;ul&gt;
62 &lt;li&gt;Initial release.&lt;/li&gt;
63 &lt;/ul&gt;
64 </content>
65 <as:version>1.0.0.0</as:version>
66 <updated>2014-11-09T12:39:00.000Z</updated>
67 </entry>
68</feed>
diff --git a/src/libs/dutil/test/DUtilUnitTest/UnitTest.rc b/src/libs/dutil/test/DUtilUnitTest/UnitTest.rc
new file mode 100644
index 00000000..14cebe1a
--- /dev/null
+++ b/src/libs/dutil/test/DUtilUnitTest/UnitTest.rc
@@ -0,0 +1,6 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#define VER_APP
4#define VER_ORIGINAL_FILENAME "UnitTest.dll"
5#define VER_INTERNAL_NAME "setup"
6#define VER_FILE_DESCRIPTION "WiX Toolset Bootstrapper unit tests"
diff --git a/src/libs/dutil/test/DUtilUnitTest/UriUtilTest.cpp b/src/libs/dutil/test/DUtilUnitTest/UriUtilTest.cpp
new file mode 100644
index 00000000..b3bf87a2
--- /dev/null
+++ b/src/libs/dutil/test/DUtilUnitTest/UriUtilTest.cpp
@@ -0,0 +1,98 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5using namespace System;
6using namespace System::Text;
7using namespace System::Collections::Generic;
8using namespace Xunit;
9
10namespace CfgTests
11{
12 public ref class UriUtil
13 {
14 public:
15 [Fact]
16 void UriProtocolTest()
17 {
18 HRESULT hr = S_OK;
19
20 DutilInitialize(&DutilTestTraceError);
21
22 LPCWSTR uri = L"https://localhost/";
23 URI_PROTOCOL uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN;
24 hr = UriProtocol(uri, &uriProtocol);
25 ExitOnFailure(hr, "Failed to determine UriProtocol");
26 Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_HTTPS, (int)uriProtocol);
27
28 uri = L"HTTPS://localhost/";
29 uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN;
30 hr = UriProtocol(uri, &uriProtocol);
31 ExitOnFailure(hr, "Failed to determine UriProtocol");
32 Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_HTTPS, (int)uriProtocol);
33
34 uri = L"HtTpS://localhost/";
35 uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN;
36 hr = UriProtocol(uri, &uriProtocol);
37 ExitOnFailure(hr, "Failed to determine UriProtocol");
38 Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_HTTPS, (int)uriProtocol);
39
40 uri = L"HTTP://localhost/";
41 uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN;
42 hr = UriProtocol(uri, &uriProtocol);
43 ExitOnFailure(hr, "Failed to determine UriProtocol");
44 Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_HTTP, (int)uriProtocol);
45
46 uri = L"http://localhost/";
47 uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN;
48 hr = UriProtocol(uri, &uriProtocol);
49 ExitOnFailure(hr, "Failed to determine UriProtocol");
50 Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_HTTP, (int)uriProtocol);
51
52 uri = L"HtTp://localhost/";
53 uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN;
54 hr = UriProtocol(uri, &uriProtocol);
55 ExitOnFailure(hr, "Failed to determine UriProtocol");
56 Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_HTTP, (int)uriProtocol);
57
58 uri = L"file://localhost/";
59 uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN;
60 hr = UriProtocol(uri, &uriProtocol);
61 ExitOnFailure(hr, "Failed to determine UriProtocol");
62 Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_FILE, (int)uriProtocol);
63
64 uri = L"FILE://localhost/";
65 uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN;
66 hr = UriProtocol(uri, &uriProtocol);
67 ExitOnFailure(hr, "Failed to determine UriProtocol");
68 Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_FILE, (int)uriProtocol);
69
70 uri = L"FiLe://localhost/";
71 uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN;
72 hr = UriProtocol(uri, &uriProtocol);
73 ExitOnFailure(hr, "Failed to determine UriProtocol");
74 Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_FILE, (int)uriProtocol);
75
76 uri = L"FTP://localhost/";
77 uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN;
78 hr = UriProtocol(uri, &uriProtocol);
79 ExitOnFailure(hr, "Failed to determine UriProtocol");
80 Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_FTP, (int)uriProtocol);
81
82 uri = L"ftp://localhost/";
83 uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN;
84 hr = UriProtocol(uri, &uriProtocol);
85 ExitOnFailure(hr, "Failed to determine UriProtocol");
86 Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_FTP, (int)uriProtocol);
87
88 uri = L"FtP://localhost/";
89 uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN;
90 hr = UriProtocol(uri, &uriProtocol);
91 ExitOnFailure(hr, "Failed to determine UriProtocol");
92 Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_FTP, (int)uriProtocol);
93
94 LExit:
95 DutilUninitialize();
96 }
97 };
98}
diff --git a/src/libs/dutil/test/DUtilUnitTest/VerUtilTests.cpp b/src/libs/dutil/test/DUtilUnitTest/VerUtilTests.cpp
new file mode 100644
index 00000000..8f24ad1a
--- /dev/null
+++ b/src/libs/dutil/test/DUtilUnitTest/VerUtilTests.cpp
@@ -0,0 +1,933 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5using namespace System;
6using namespace Xunit;
7using namespace WixBuildTools::TestSupport;
8
9namespace DutilTests
10{
11 public ref class VerUtil
12 {
13 public:
14 [Fact]
15 void VerCompareVersionsTreatsMissingRevisionAsZero()
16 {
17 HRESULT hr = S_OK;
18 VERUTIL_VERSION* pVersion1 = NULL;
19 VERUTIL_VERSION* pVersion2 = NULL;
20 VERUTIL_VERSION* pVersion3 = NULL;
21 LPCWSTR wzVersion1 = L"1.2.3.4";
22 LPCWSTR wzVersion2 = L"1.2.3";
23 LPCWSTR wzVersion3 = L"1.2.3.0";
24
25 try
26 {
27 hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1);
28 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1);
29
30 hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2);
31 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2);
32
33 hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3);
34 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3);
35
36 NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion);
37 Assert::Equal<DWORD>(1, pVersion1->dwMajor);
38 Assert::Equal<DWORD>(2, pVersion1->dwMinor);
39 Assert::Equal<DWORD>(3, pVersion1->dwPatch);
40 Assert::Equal<DWORD>(4, pVersion1->dwRevision);
41 Assert::Equal<DWORD>(0, pVersion1->cReleaseLabels);
42 Assert::Equal<DWORD>(7, pVersion1->cchMetadataOffset);
43 Assert::Equal<BOOL>(FALSE, pVersion1->fInvalid);
44
45 NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion);
46 Assert::Equal<DWORD>(1, pVersion2->dwMajor);
47 Assert::Equal<DWORD>(2, pVersion2->dwMinor);
48 Assert::Equal<DWORD>(3, pVersion2->dwPatch);
49 Assert::Equal<DWORD>(0, pVersion2->dwRevision);
50 Assert::Equal<DWORD>(0, pVersion2->cReleaseLabels);
51 Assert::Equal<DWORD>(5, pVersion2->cchMetadataOffset);
52 Assert::Equal<BOOL>(FALSE, pVersion2->fInvalid);
53
54 NativeAssert::StringEqual(wzVersion3, pVersion3->sczVersion);
55 Assert::Equal<DWORD>(1, pVersion3->dwMajor);
56 Assert::Equal<DWORD>(2, pVersion3->dwMinor);
57 Assert::Equal<DWORD>(3, pVersion3->dwPatch);
58 Assert::Equal<DWORD>(0, pVersion3->dwRevision);
59 Assert::Equal<DWORD>(0, pVersion3->cReleaseLabels);
60 Assert::Equal<DWORD>(7, pVersion3->cchMetadataOffset);
61 Assert::Equal<BOOL>(FALSE, pVersion3->fInvalid);
62
63 TestVerutilCompareParsedVersions(pVersion1, pVersion2, 1);
64 TestVerutilCompareParsedVersions(pVersion3, pVersion2, 0);
65 }
66 finally
67 {
68 ReleaseVerutilVersion(pVersion1);
69 ReleaseVerutilVersion(pVersion2);
70 ReleaseVerutilVersion(pVersion3);
71 }
72 }
73
74 [Fact]
75 void VerCompareVersionsTreatsNumericReleaseLabelsAsNumbers()
76 {
77 HRESULT hr = S_OK;
78 VERUTIL_VERSION* pVersion1 = NULL;
79 VERUTIL_VERSION* pVersion2 = NULL;
80 LPCWSTR wzVersion1 = L"1.0-2.0";
81 LPCWSTR wzVersion2 = L"1.0-19";
82
83 try
84 {
85 hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1);
86 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1);
87
88 hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2);
89 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2);
90
91 NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion);
92 Assert::Equal<DWORD>(1, pVersion1->dwMajor);
93 Assert::Equal<DWORD>(0, pVersion1->dwMinor);
94 Assert::Equal<DWORD>(0, pVersion1->dwPatch);
95 Assert::Equal<DWORD>(0, pVersion1->dwRevision);
96 Assert::Equal<DWORD>(2, pVersion1->cReleaseLabels);
97
98 Assert::Equal<BOOL>(TRUE, pVersion1->rgReleaseLabels[0].fNumeric);
99 Assert::Equal<DWORD>(2, pVersion1->rgReleaseLabels[0].dwValue);
100 Assert::Equal<DWORD>(1, pVersion1->rgReleaseLabels[0].cchLabel);
101 Assert::Equal<DWORD>(4, pVersion1->rgReleaseLabels[0].cchLabelOffset);
102
103 Assert::Equal<BOOL>(TRUE, pVersion1->rgReleaseLabels[1].fNumeric);
104 Assert::Equal<DWORD>(0, pVersion1->rgReleaseLabels[1].dwValue);
105 Assert::Equal<DWORD>(1, pVersion1->rgReleaseLabels[1].cchLabel);
106 Assert::Equal<DWORD>(6, pVersion1->rgReleaseLabels[1].cchLabelOffset);
107
108 Assert::Equal<DWORD>(7, pVersion1->cchMetadataOffset);
109 Assert::Equal<BOOL>(FALSE, pVersion1->fInvalid);
110
111 NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion);
112 Assert::Equal<DWORD>(1, pVersion2->dwMajor);
113 Assert::Equal<DWORD>(0, pVersion2->dwMinor);
114 Assert::Equal<DWORD>(0, pVersion2->dwPatch);
115 Assert::Equal<DWORD>(0, pVersion2->dwRevision);
116 Assert::Equal<DWORD>(1, pVersion2->cReleaseLabels);
117
118 Assert::Equal<BOOL>(TRUE, pVersion2->rgReleaseLabels[0].fNumeric);
119 Assert::Equal<DWORD>(19, pVersion2->rgReleaseLabels[0].dwValue);
120 Assert::Equal<DWORD>(2, pVersion2->rgReleaseLabels[0].cchLabel);
121 Assert::Equal<DWORD>(4, pVersion2->rgReleaseLabels[0].cchLabelOffset);
122
123 Assert::Equal<DWORD>(6, pVersion2->cchMetadataOffset);
124 Assert::Equal<BOOL>(FALSE, pVersion2->fInvalid);
125
126 TestVerutilCompareParsedVersions(pVersion1, pVersion2, -1);
127 }
128 finally
129 {
130 ReleaseVerutilVersion(pVersion1);
131 ReleaseVerutilVersion(pVersion2);
132 }
133 }
134
135 [Fact]
136 void VerCompareVersionsHandlesNormallyInvalidVersions()
137 {
138 HRESULT hr = S_OK;
139 VERUTIL_VERSION* pVersion1 = NULL;
140 VERUTIL_VERSION* pVersion2 = NULL;
141 VERUTIL_VERSION* pVersion3 = NULL;
142 VERUTIL_VERSION* pVersion4 = NULL;
143 VERUTIL_VERSION* pVersion5 = NULL;
144 VERUTIL_VERSION* pVersion6 = NULL;
145 LPCWSTR wzVersion1 = L"10.-4.0";
146 LPCWSTR wzVersion2 = L"10.-2.0";
147 LPCWSTR wzVersion3 = L"0";
148 LPCWSTR wzVersion4 = L"";
149 LPCWSTR wzVersion5 = L"10-2";
150 LPCWSTR wzVersion6 = L"10-4.@";
151
152 try
153 {
154 hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1);
155 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1);
156
157 hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2);
158 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2);
159
160 hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3);
161 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3);
162
163 hr = VerParseVersion(wzVersion4, 0, FALSE, &pVersion4);
164 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion4);
165
166 hr = VerParseVersion(wzVersion5, 0, FALSE, &pVersion5);
167 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion5);
168
169 hr = VerParseVersion(wzVersion6, 0, FALSE, &pVersion6);
170 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion6);
171
172 NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion);
173 Assert::Equal<DWORD>(10, pVersion1->dwMajor);
174 Assert::Equal<DWORD>(0, pVersion1->dwMinor);
175 Assert::Equal<DWORD>(0, pVersion1->dwPatch);
176 Assert::Equal<DWORD>(0, pVersion1->dwRevision);
177 Assert::Equal<DWORD>(0, pVersion1->cReleaseLabels);
178 Assert::Equal<DWORD>(3, pVersion1->cchMetadataOffset);
179 Assert::Equal<BOOL>(TRUE, pVersion1->fInvalid);
180
181 NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion);
182 Assert::Equal<DWORD>(10, pVersion2->dwMajor);
183 Assert::Equal<DWORD>(0, pVersion2->dwMinor);
184 Assert::Equal<DWORD>(0, pVersion2->dwPatch);
185 Assert::Equal<DWORD>(0, pVersion2->dwRevision);
186 Assert::Equal<DWORD>(0, pVersion2->cReleaseLabels);
187 Assert::Equal<DWORD>(3, pVersion2->cchMetadataOffset);
188 Assert::Equal<BOOL>(TRUE, pVersion2->fInvalid);
189
190 NativeAssert::StringEqual(wzVersion3, pVersion3->sczVersion);
191 Assert::Equal<DWORD>(0, pVersion3->dwMajor);
192 Assert::Equal<DWORD>(0, pVersion3->dwMinor);
193 Assert::Equal<DWORD>(0, pVersion3->dwPatch);
194 Assert::Equal<DWORD>(0, pVersion3->dwRevision);
195 Assert::Equal<DWORD>(0, pVersion3->cReleaseLabels);
196 Assert::Equal<DWORD>(1, pVersion3->cchMetadataOffset);
197 Assert::Equal<BOOL>(FALSE, pVersion3->fInvalid);
198
199 NativeAssert::StringEqual(wzVersion4, pVersion4->sczVersion);
200 Assert::Equal<DWORD>(0, pVersion4->dwMajor);
201 Assert::Equal<DWORD>(0, pVersion4->dwMinor);
202 Assert::Equal<DWORD>(0, pVersion4->dwPatch);
203 Assert::Equal<DWORD>(0, pVersion4->dwRevision);
204 Assert::Equal<DWORD>(0, pVersion4->cReleaseLabels);
205 Assert::Equal<DWORD>(0, pVersion4->cchMetadataOffset);
206 Assert::Equal<BOOL>(TRUE, pVersion4->fInvalid);
207
208 NativeAssert::StringEqual(wzVersion5, pVersion5->sczVersion);
209 Assert::Equal<DWORD>(10, pVersion5->dwMajor);
210 Assert::Equal<DWORD>(0, pVersion5->dwMinor);
211 Assert::Equal<DWORD>(0, pVersion5->dwPatch);
212 Assert::Equal<DWORD>(0, pVersion5->dwRevision);
213 Assert::Equal<DWORD>(1, pVersion5->cReleaseLabels);
214
215 Assert::Equal<BOOL>(TRUE, pVersion5->rgReleaseLabels[0].fNumeric);
216 Assert::Equal<DWORD>(2, pVersion5->rgReleaseLabels[0].dwValue);
217 Assert::Equal<DWORD>(1, pVersion5->rgReleaseLabels[0].cchLabel);
218 Assert::Equal<DWORD>(3, pVersion5->rgReleaseLabels[0].cchLabelOffset);
219
220 Assert::Equal<DWORD>(4, pVersion5->cchMetadataOffset);
221 Assert::Equal<BOOL>(FALSE, pVersion5->fInvalid);
222
223 NativeAssert::StringEqual(wzVersion6, pVersion6->sczVersion);
224 Assert::Equal<DWORD>(10, pVersion6->dwMajor);
225 Assert::Equal<DWORD>(0, pVersion6->dwMinor);
226 Assert::Equal<DWORD>(0, pVersion6->dwPatch);
227 Assert::Equal<DWORD>(0, pVersion6->dwRevision);
228 Assert::Equal<DWORD>(1, pVersion6->cReleaseLabels);
229
230 Assert::Equal<BOOL>(TRUE, pVersion6->rgReleaseLabels[0].fNumeric);
231 Assert::Equal<DWORD>(4, pVersion6->rgReleaseLabels[0].dwValue);
232 Assert::Equal<DWORD>(1, pVersion6->rgReleaseLabels[0].cchLabel);
233 Assert::Equal<DWORD>(3, pVersion6->rgReleaseLabels[0].cchLabelOffset);
234
235 Assert::Equal<DWORD>(5, pVersion6->cchMetadataOffset);
236 Assert::Equal<BOOL>(TRUE, pVersion6->fInvalid);
237
238 TestVerutilCompareParsedVersions(pVersion1, pVersion2, 1);
239 TestVerutilCompareParsedVersions(pVersion3, pVersion4, 1);
240 TestVerutilCompareParsedVersions(pVersion5, pVersion6, -1);
241 }
242 finally
243 {
244 ReleaseVerutilVersion(pVersion1);
245 ReleaseVerutilVersion(pVersion2);
246 ReleaseVerutilVersion(pVersion3);
247 ReleaseVerutilVersion(pVersion4);
248 ReleaseVerutilVersion(pVersion5);
249 ReleaseVerutilVersion(pVersion6);
250 }
251 }
252
253 [Fact]
254 void VerCompareVersionsTreatsHyphenAsVersionSeparator()
255 {
256 HRESULT hr = S_OK;
257 VERUTIL_VERSION* pVersion1 = NULL;
258 VERUTIL_VERSION* pVersion2 = NULL;
259 VERUTIL_VERSION* pVersion3 = NULL;
260 LPCWSTR wzVersion1 = L"0.0.1-a";
261 LPCWSTR wzVersion2 = L"0-2";
262 LPCWSTR wzVersion3 = L"1-2";
263
264 try
265 {
266 hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1);
267 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1);
268
269 hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2);
270 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2);
271
272 hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3);
273 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3);
274
275 NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion);
276 Assert::Equal<DWORD>(0, pVersion1->dwMajor);
277 Assert::Equal<DWORD>(0, pVersion1->dwMinor);
278 Assert::Equal<DWORD>(1, pVersion1->dwPatch);
279 Assert::Equal<DWORD>(0, pVersion1->dwRevision);
280 Assert::Equal<DWORD>(1, pVersion1->cReleaseLabels);
281
282 Assert::Equal<BOOL>(FALSE, pVersion1->rgReleaseLabels[0].fNumeric);
283 Assert::Equal<DWORD>(1, pVersion1->rgReleaseLabels[0].cchLabel);
284 Assert::Equal<DWORD>(6, pVersion1->rgReleaseLabels[0].cchLabelOffset);
285
286 Assert::Equal<DWORD>(7, pVersion1->cchMetadataOffset);
287 Assert::Equal<BOOL>(FALSE, pVersion1->fInvalid);
288
289 NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion);
290 Assert::Equal<DWORD>(0, pVersion2->dwMajor);
291 Assert::Equal<DWORD>(0, pVersion2->dwMinor);
292 Assert::Equal<DWORD>(0, pVersion2->dwPatch);
293 Assert::Equal<DWORD>(0, pVersion2->dwRevision);
294 Assert::Equal<DWORD>(1, pVersion2->cReleaseLabels);
295
296 Assert::Equal<BOOL>(TRUE, pVersion2->rgReleaseLabels[0].fNumeric);
297 Assert::Equal<DWORD>(2, pVersion2->rgReleaseLabels[0].dwValue);
298 Assert::Equal<DWORD>(1, pVersion2->rgReleaseLabels[0].cchLabel);
299 Assert::Equal<DWORD>(2, pVersion2->rgReleaseLabels[0].cchLabelOffset);
300
301 Assert::Equal<DWORD>(3, pVersion2->cchMetadataOffset);
302 Assert::Equal<BOOL>(FALSE, pVersion2->fInvalid);
303
304 NativeAssert::StringEqual(wzVersion3, pVersion3->sczVersion);
305 Assert::Equal<DWORD>(1, pVersion3->dwMajor);
306 Assert::Equal<DWORD>(0, pVersion3->dwMinor);
307 Assert::Equal<DWORD>(0, pVersion3->dwPatch);
308 Assert::Equal<DWORD>(0, pVersion3->dwRevision);
309 Assert::Equal<DWORD>(1, pVersion3->cReleaseLabels);
310
311 Assert::Equal<BOOL>(TRUE, pVersion3->rgReleaseLabels[0].fNumeric);
312 Assert::Equal<DWORD>(2, pVersion3->rgReleaseLabels[0].dwValue);
313 Assert::Equal<DWORD>(1, pVersion3->rgReleaseLabels[0].cchLabel);
314 Assert::Equal<DWORD>(2, pVersion3->rgReleaseLabels[0].cchLabelOffset);
315
316 Assert::Equal<DWORD>(3, pVersion3->cchMetadataOffset);
317 Assert::Equal<BOOL>(FALSE, pVersion3->fInvalid);
318
319 TestVerutilCompareParsedVersions(pVersion1, pVersion2, 1);
320 TestVerutilCompareParsedVersions(pVersion1, pVersion3, -1);
321 }
322 finally
323 {
324 ReleaseVerutilVersion(pVersion1);
325 ReleaseVerutilVersion(pVersion2);
326 ReleaseVerutilVersion(pVersion3);
327 }
328 }
329
330 [Fact]
331 void VerCompareVersionsIgnoresLeadingZeroes()
332 {
333 HRESULT hr = S_OK;
334 VERUTIL_VERSION* pVersion1 = NULL;
335 VERUTIL_VERSION* pVersion2 = NULL;
336 VERUTIL_VERSION* pVersion3 = NULL;
337 VERUTIL_VERSION* pVersion4 = NULL;
338 LPCWSTR wzVersion1 = L"0.01-a.1";
339 LPCWSTR wzVersion2 = L"0.1.0-a.1";
340 LPCWSTR wzVersion3 = L"0.1-a.b.0";
341 LPCWSTR wzVersion4 = L"0.1.0-a.b.000";
342
343 try
344 {
345 hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1);
346 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1);
347
348 hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2);
349 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2);
350
351 hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3);
352 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3);
353
354 hr = VerParseVersion(wzVersion4, 0, FALSE, &pVersion4);
355 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion4);
356
357 NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion);
358 Assert::Equal<DWORD>(0, pVersion1->dwMajor);
359 Assert::Equal<DWORD>(1, pVersion1->dwMinor);
360 Assert::Equal<DWORD>(0, pVersion1->dwPatch);
361 Assert::Equal<DWORD>(0, pVersion1->dwRevision);
362 Assert::Equal<DWORD>(2, pVersion1->cReleaseLabels);
363
364 Assert::Equal<BOOL>(FALSE, pVersion1->rgReleaseLabels[0].fNumeric);
365 Assert::Equal<DWORD>(1, pVersion1->rgReleaseLabels[0].cchLabel);
366 Assert::Equal<DWORD>(5, pVersion1->rgReleaseLabels[0].cchLabelOffset);
367
368 Assert::Equal<BOOL>(TRUE, pVersion1->rgReleaseLabels[1].fNumeric);
369 Assert::Equal<DWORD>(1, pVersion1->rgReleaseLabels[1].dwValue);
370 Assert::Equal<DWORD>(1, pVersion1->rgReleaseLabels[1].cchLabel);
371 Assert::Equal<DWORD>(7, pVersion1->rgReleaseLabels[1].cchLabelOffset);
372
373 Assert::Equal<DWORD>(8, pVersion1->cchMetadataOffset);
374 Assert::Equal<BOOL>(FALSE, pVersion1->fInvalid);
375
376 NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion);
377 Assert::Equal<DWORD>(0, pVersion2->dwMajor);
378 Assert::Equal<DWORD>(1, pVersion2->dwMinor);
379 Assert::Equal<DWORD>(0, pVersion2->dwPatch);
380 Assert::Equal<DWORD>(0, pVersion2->dwRevision);
381 Assert::Equal<DWORD>(2, pVersion2->cReleaseLabels);
382
383 Assert::Equal<BOOL>(FALSE, pVersion2->rgReleaseLabels[0].fNumeric);
384 Assert::Equal<DWORD>(1, pVersion2->rgReleaseLabels[0].cchLabel);
385 Assert::Equal<DWORD>(6, pVersion2->rgReleaseLabels[0].cchLabelOffset);
386
387 Assert::Equal<BOOL>(TRUE, pVersion2->rgReleaseLabels[1].fNumeric);
388 Assert::Equal<DWORD>(1, pVersion2->rgReleaseLabels[1].dwValue);
389 Assert::Equal<DWORD>(1, pVersion2->rgReleaseLabels[1].cchLabel);
390 Assert::Equal<DWORD>(8, pVersion2->rgReleaseLabels[1].cchLabelOffset);
391
392 Assert::Equal<DWORD>(9, pVersion2->cchMetadataOffset);
393 Assert::Equal<BOOL>(FALSE, pVersion2->fInvalid);
394
395 NativeAssert::StringEqual(wzVersion3, pVersion3->sczVersion);
396 Assert::Equal<DWORD>(0, pVersion3->dwMajor);
397 Assert::Equal<DWORD>(1, pVersion3->dwMinor);
398 Assert::Equal<DWORD>(0, pVersion3->dwPatch);
399 Assert::Equal<DWORD>(0, pVersion3->dwRevision);
400 Assert::Equal<DWORD>(3, pVersion3->cReleaseLabels);
401
402 Assert::Equal<BOOL>(FALSE, pVersion3->rgReleaseLabels[0].fNumeric);
403 Assert::Equal<DWORD>(1, pVersion3->rgReleaseLabels[0].cchLabel);
404 Assert::Equal<DWORD>(4, pVersion3->rgReleaseLabels[0].cchLabelOffset);
405
406 Assert::Equal<BOOL>(FALSE, pVersion3->rgReleaseLabels[1].fNumeric);
407 Assert::Equal<DWORD>(1, pVersion3->rgReleaseLabels[1].cchLabel);
408 Assert::Equal<DWORD>(6, pVersion3->rgReleaseLabels[1].cchLabelOffset);
409
410 Assert::Equal<BOOL>(TRUE, pVersion3->rgReleaseLabels[2].fNumeric);
411 Assert::Equal<DWORD>(0, pVersion3->rgReleaseLabels[2].dwValue);
412 Assert::Equal<DWORD>(1, pVersion3->rgReleaseLabels[2].cchLabel);
413 Assert::Equal<DWORD>(8, pVersion3->rgReleaseLabels[2].cchLabelOffset);
414
415 Assert::Equal<DWORD>(9, pVersion3->cchMetadataOffset);
416 Assert::Equal<BOOL>(FALSE, pVersion3->fInvalid);
417
418 NativeAssert::StringEqual(wzVersion4, pVersion4->sczVersion);
419 Assert::Equal<DWORD>(0, pVersion4->dwMajor);
420 Assert::Equal<DWORD>(1, pVersion4->dwMinor);
421 Assert::Equal<DWORD>(0, pVersion4->dwPatch);
422 Assert::Equal<DWORD>(0, pVersion4->dwRevision);
423 Assert::Equal<DWORD>(3, pVersion4->cReleaseLabels);
424
425 Assert::Equal<BOOL>(FALSE, pVersion4->rgReleaseLabels[0].fNumeric);
426 Assert::Equal<DWORD>(1, pVersion4->rgReleaseLabels[0].cchLabel);
427 Assert::Equal<DWORD>(6, pVersion4->rgReleaseLabels[0].cchLabelOffset);
428
429 Assert::Equal<BOOL>(FALSE, pVersion4->rgReleaseLabels[1].fNumeric);
430 Assert::Equal<DWORD>(1, pVersion4->rgReleaseLabels[1].cchLabel);
431 Assert::Equal<DWORD>(8, pVersion4->rgReleaseLabels[1].cchLabelOffset);
432
433 Assert::Equal<BOOL>(TRUE, pVersion4->rgReleaseLabels[2].fNumeric);
434 Assert::Equal<DWORD>(0, pVersion4->rgReleaseLabels[2].dwValue);
435 Assert::Equal<DWORD>(3, pVersion4->rgReleaseLabels[2].cchLabel);
436 Assert::Equal<DWORD>(10, pVersion4->rgReleaseLabels[2].cchLabelOffset);
437
438 Assert::Equal<DWORD>(13, pVersion4->cchMetadataOffset);
439 Assert::Equal<BOOL>(FALSE, pVersion4->fInvalid);
440
441 TestVerutilCompareParsedVersions(pVersion1, pVersion2, 0);
442 TestVerutilCompareParsedVersions(pVersion3, pVersion4, 0);
443 }
444 finally
445 {
446 ReleaseVerutilVersion(pVersion1);
447 ReleaseVerutilVersion(pVersion2);
448 ReleaseVerutilVersion(pVersion3);
449 ReleaseVerutilVersion(pVersion4);
450 }
451 }
452
453 [Fact]
454 void VerCompareVersionsTreatsUnexpectedContentAsMetadata()
455 {
456 HRESULT hr = S_OK;
457 VERUTIL_VERSION* pVersion1 = NULL;
458 VERUTIL_VERSION* pVersion2 = NULL;
459 VERUTIL_VERSION* pVersion3 = NULL;
460 LPCWSTR wzVersion1 = L"1.2.3+abcd";
461 LPCWSTR wzVersion2 = L"1.2.3.abcd";
462 LPCWSTR wzVersion3 = L"1.2.3.-abcd";
463
464 try
465 {
466 hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1);
467 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1);
468
469 hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2);
470 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2);
471
472 hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3);
473 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3);
474
475 NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion);
476 Assert::Equal<DWORD>(1, pVersion1->dwMajor);
477 Assert::Equal<DWORD>(2, pVersion1->dwMinor);
478 Assert::Equal<DWORD>(3, pVersion1->dwPatch);
479 Assert::Equal<DWORD>(0, pVersion1->dwRevision);
480 Assert::Equal<DWORD>(0, pVersion1->cReleaseLabels);
481 Assert::Equal<DWORD>(6, pVersion1->cchMetadataOffset);
482 Assert::Equal<BOOL>(FALSE, pVersion1->fInvalid);
483
484 NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion);
485 Assert::Equal<DWORD>(1, pVersion2->dwMajor);
486 Assert::Equal<DWORD>(2, pVersion2->dwMinor);
487 Assert::Equal<DWORD>(3, pVersion2->dwPatch);
488 Assert::Equal<DWORD>(0, pVersion2->dwRevision);
489 Assert::Equal<DWORD>(0, pVersion2->cReleaseLabels);
490 Assert::Equal<DWORD>(6, pVersion2->cchMetadataOffset);
491 Assert::Equal<BOOL>(TRUE, pVersion2->fInvalid);
492
493 NativeAssert::StringEqual(wzVersion3, pVersion3->sczVersion);
494 Assert::Equal<DWORD>(1, pVersion3->dwMajor);
495 Assert::Equal<DWORD>(2, pVersion3->dwMinor);
496 Assert::Equal<DWORD>(3, pVersion3->dwPatch);
497 Assert::Equal<DWORD>(0, pVersion3->dwRevision);
498 Assert::Equal<DWORD>(0, pVersion3->cReleaseLabels);
499 Assert::Equal<DWORD>(6, pVersion3->cchMetadataOffset);
500 Assert::Equal<BOOL>(TRUE, pVersion3->fInvalid);
501
502 TestVerutilCompareParsedVersions(pVersion1, pVersion2, 1);
503 TestVerutilCompareParsedVersions(pVersion1, pVersion3, 1);
504 TestVerutilCompareParsedVersions(pVersion2, pVersion3, -1);
505 }
506 finally
507 {
508 ReleaseVerutilVersion(pVersion1);
509 ReleaseVerutilVersion(pVersion2);
510 ReleaseVerutilVersion(pVersion3);
511 }
512 }
513
514 [Fact]
515 void VerCompareVersionsIgnoresLeadingV()
516 {
517 HRESULT hr = S_OK;
518 VERUTIL_VERSION* pVersion1 = NULL;
519 VERUTIL_VERSION* pVersion2 = NULL;
520 VERUTIL_VERSION* pVersion3 = NULL;
521 LPCWSTR wzVersion1 = L"10.20.30.40";
522 LPCWSTR wzVersion2 = L"v10.20.30.40";
523 LPCWSTR wzVersion3 = L"V10.20.30.40";
524
525 try
526 {
527 hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1);
528 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1);
529
530 hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2);
531 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2);
532
533 hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3);
534 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3);
535
536 NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion);
537 Assert::Equal<DWORD>(10, pVersion1->dwMajor);
538 Assert::Equal<DWORD>(20, pVersion1->dwMinor);
539 Assert::Equal<DWORD>(30, pVersion1->dwPatch);
540 Assert::Equal<DWORD>(40, pVersion1->dwRevision);
541 Assert::Equal<DWORD>(0, pVersion1->cReleaseLabels);
542 Assert::Equal<DWORD>(11, pVersion1->cchMetadataOffset);
543 Assert::Equal<BOOL>(FALSE, pVersion1->fInvalid);
544
545 NativeAssert::StringEqual(wzVersion1, pVersion2->sczVersion);
546 Assert::Equal<DWORD>(10, pVersion2->dwMajor);
547 Assert::Equal<DWORD>(20, pVersion2->dwMinor);
548 Assert::Equal<DWORD>(30, pVersion2->dwPatch);
549 Assert::Equal<DWORD>(40, pVersion2->dwRevision);
550 Assert::Equal<DWORD>(0, pVersion2->cReleaseLabels);
551 Assert::Equal<DWORD>(11, pVersion2->cchMetadataOffset);
552 Assert::Equal<BOOL>(FALSE, pVersion2->fInvalid);
553
554 NativeAssert::StringEqual(wzVersion1, pVersion3->sczVersion);
555 Assert::Equal<DWORD>(10, pVersion3->dwMajor);
556 Assert::Equal<DWORD>(20, pVersion3->dwMinor);
557 Assert::Equal<DWORD>(30, pVersion3->dwPatch);
558 Assert::Equal<DWORD>(40, pVersion3->dwRevision);
559 Assert::Equal<DWORD>(0, pVersion3->cReleaseLabels);
560 Assert::Equal<DWORD>(11, pVersion3->cchMetadataOffset);
561 Assert::Equal<BOOL>(FALSE, pVersion3->fInvalid);
562
563 TestVerutilCompareParsedVersions(pVersion1, pVersion2, 0);
564 TestVerutilCompareParsedVersions(pVersion1, pVersion3, 0);
565 }
566 finally
567 {
568 ReleaseVerutilVersion(pVersion1);
569 ReleaseVerutilVersion(pVersion2);
570 ReleaseVerutilVersion(pVersion3);
571 }
572 }
573
574 [Fact]
575 void VerCompareVersionsHandlesTooLargeNumbers()
576 {
577 HRESULT hr = S_OK;
578 VERUTIL_VERSION* pVersion1 = NULL;
579 VERUTIL_VERSION* pVersion2 = NULL;
580 LPCWSTR wzVersion1 = L"4294967295.4294967295.4294967295.4294967295";
581 LPCWSTR wzVersion2 = L"4294967296.4294967296.4294967296.4294967296";
582
583 try
584 {
585 hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1);
586 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1);
587
588 hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2);
589 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2);
590
591 NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion);
592 Assert::Equal<DWORD>(4294967295, pVersion1->dwMajor);
593 Assert::Equal<DWORD>(4294967295, pVersion1->dwMinor);
594 Assert::Equal<DWORD>(4294967295, pVersion1->dwPatch);
595 Assert::Equal<DWORD>(4294967295, pVersion1->dwRevision);
596 Assert::Equal<DWORD>(0, pVersion1->cReleaseLabels);
597 Assert::Equal<DWORD>(43, pVersion1->cchMetadataOffset);
598 Assert::Equal<BOOL>(FALSE, pVersion1->fInvalid);
599
600 NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion);
601 Assert::Equal<DWORD>(0, pVersion2->dwMajor);
602 Assert::Equal<DWORD>(0, pVersion2->dwMinor);
603 Assert::Equal<DWORD>(0, pVersion2->dwPatch);
604 Assert::Equal<DWORD>(0, pVersion2->dwRevision);
605 Assert::Equal<DWORD>(0, pVersion2->cReleaseLabels);
606 Assert::Equal<DWORD>(0, pVersion2->cchMetadataOffset);
607 Assert::Equal<BOOL>(TRUE, pVersion2->fInvalid);
608
609 TestVerutilCompareParsedVersions(pVersion1, pVersion2, 1);
610 }
611 finally
612 {
613 ReleaseVerutilVersion(pVersion1);
614 ReleaseVerutilVersion(pVersion2);
615 }
616 }
617
618 [Fact]
619 void VerCompareVersionsIgnoresMetadataForValidVersions()
620 {
621 HRESULT hr = S_OK;
622 VERUTIL_VERSION* pVersion1 = NULL;
623 VERUTIL_VERSION* pVersion2 = NULL;
624 LPCWSTR wzVersion1 = L"1.2.3+abc";
625 LPCWSTR wzVersion2 = L"1.2.3+xyz";
626
627 try
628 {
629 hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1);
630 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1);
631
632 hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2);
633 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2);
634
635 NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion);
636 Assert::Equal<DWORD>(1, pVersion1->dwMajor);
637 Assert::Equal<DWORD>(2, pVersion1->dwMinor);
638 Assert::Equal<DWORD>(3, pVersion1->dwPatch);
639 Assert::Equal<DWORD>(0, pVersion1->dwRevision);
640 Assert::Equal<DWORD>(0, pVersion1->cReleaseLabels);
641 Assert::Equal<DWORD>(6, pVersion1->cchMetadataOffset);
642 Assert::Equal<BOOL>(FALSE, pVersion1->fInvalid);
643
644 NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion);
645 Assert::Equal<DWORD>(1, pVersion2->dwMajor);
646 Assert::Equal<DWORD>(2, pVersion2->dwMinor);
647 Assert::Equal<DWORD>(3, pVersion2->dwPatch);
648 Assert::Equal<DWORD>(0, pVersion2->dwRevision);
649 Assert::Equal<DWORD>(0, pVersion2->cReleaseLabels);
650 Assert::Equal<DWORD>(6, pVersion2->cchMetadataOffset);
651 Assert::Equal<BOOL>(FALSE, pVersion2->fInvalid);
652
653 TestVerutilCompareParsedVersions(pVersion1, pVersion2, 0);
654 }
655 finally
656 {
657 ReleaseVerutilVersion(pVersion1);
658 ReleaseVerutilVersion(pVersion2);
659 }
660 }
661
662 [Fact]
663 void VerCopyVersionCopiesVersion()
664 {
665 HRESULT hr = S_OK;
666 LPCWSTR wzVersion = L"1.2.3.4+abc123";
667 VERUTIL_VERSION* pSource = NULL;
668 VERUTIL_VERSION* pCopy = NULL;
669 int nResult = 0;
670
671 try
672 {
673 hr = VerParseVersion(wzVersion, 0, FALSE, &pSource);
674 NativeAssert::Succeeded(hr, "VerParseVersion failed");
675
676 NativeAssert::StringEqual(wzVersion, pSource->sczVersion);
677 Assert::Equal<DWORD>(1, pSource->dwMajor);
678 Assert::Equal<DWORD>(2, pSource->dwMinor);
679 Assert::Equal<DWORD>(3, pSource->dwPatch);
680 Assert::Equal<DWORD>(4, pSource->dwRevision);
681 Assert::Equal<DWORD>(0, pSource->cReleaseLabels);
682
683 Assert::Equal<DWORD>(8, pSource->cchMetadataOffset);
684 Assert::Equal<BOOL>(FALSE, pSource->fInvalid);
685
686 hr = VerCopyVersion(pSource, &pCopy);
687 NativeAssert::Succeeded(hr, "VerCopyVersion failed");
688
689 Assert::False(pSource == pCopy);
690 Assert::False(pSource->sczVersion == pCopy->sczVersion);
691
692 hr = VerCompareParsedVersions(pSource, pCopy, &nResult);
693 NativeAssert::Succeeded(hr, "VerCompareParsedVersions failed");
694
695 Assert::Equal<int>(nResult, 0);
696 }
697 finally
698 {
699 ReleaseVerutilVersion(pCopy);
700 ReleaseVerutilVersion(pSource);
701 }
702 }
703
704 [Fact]
705 void VerCopyVersionCopiesPrereleaseVersion()
706 {
707 HRESULT hr = S_OK;
708 LPCWSTR wzVersion = L"1.2.3.4-a.b.c.d.5.+abc123";
709 VERUTIL_VERSION* pSource = NULL;
710 VERUTIL_VERSION* pCopy = NULL;
711 int nResult = 0;
712
713 try
714 {
715 hr = VerParseVersion(wzVersion, 0, FALSE, &pSource);
716 NativeAssert::Succeeded(hr, "VerParseVersion failed");
717
718 NativeAssert::StringEqual(wzVersion, pSource->sczVersion);
719 Assert::Equal<DWORD>(1, pSource->dwMajor);
720 Assert::Equal<DWORD>(2, pSource->dwMinor);
721 Assert::Equal<DWORD>(3, pSource->dwPatch);
722 Assert::Equal<DWORD>(4, pSource->dwRevision);
723 Assert::Equal<DWORD>(5, pSource->cReleaseLabels);
724
725 Assert::Equal<BOOL>(FALSE, pSource->rgReleaseLabels[0].fNumeric);
726 Assert::Equal<DWORD>(1, pSource->rgReleaseLabels[0].cchLabel);
727 Assert::Equal<DWORD>(8, pSource->rgReleaseLabels[0].cchLabelOffset);
728
729 Assert::Equal<BOOL>(FALSE, pSource->rgReleaseLabels[1].fNumeric);
730 Assert::Equal<DWORD>(1, pSource->rgReleaseLabels[1].cchLabel);
731 Assert::Equal<DWORD>(10, pSource->rgReleaseLabels[1].cchLabelOffset);
732
733 Assert::Equal<BOOL>(FALSE, pSource->rgReleaseLabels[2].fNumeric);
734 Assert::Equal<DWORD>(1, pSource->rgReleaseLabels[2].cchLabel);
735 Assert::Equal<DWORD>(12, pSource->rgReleaseLabels[2].cchLabelOffset);
736
737 Assert::Equal<BOOL>(FALSE, pSource->rgReleaseLabels[3].fNumeric);
738 Assert::Equal<DWORD>(1, pSource->rgReleaseLabels[3].cchLabel);
739 Assert::Equal<DWORD>(14, pSource->rgReleaseLabels[3].cchLabelOffset);
740
741 Assert::Equal<BOOL>(TRUE, pSource->rgReleaseLabels[4].fNumeric);
742 Assert::Equal<DWORD>(5, pSource->rgReleaseLabels[4].dwValue);
743 Assert::Equal<DWORD>(1, pSource->rgReleaseLabels[4].cchLabel);
744 Assert::Equal<DWORD>(16, pSource->rgReleaseLabels[4].cchLabelOffset);
745
746 Assert::Equal<DWORD>(18, pSource->cchMetadataOffset);
747 Assert::Equal<BOOL>(TRUE, pSource->fInvalid);
748
749 hr = VerCopyVersion(pSource, &pCopy);
750 NativeAssert::Succeeded(hr, "VerCopyVersion failed");
751
752 Assert::False(pSource == pCopy);
753 Assert::False(pSource->sczVersion == pCopy->sczVersion);
754 Assert::False(pSource->rgReleaseLabels == pCopy->rgReleaseLabels);
755
756 hr = VerCompareParsedVersions(pSource, pCopy, &nResult);
757 NativeAssert::Succeeded(hr, "VerCompareParsedVersions failed");
758
759 Assert::Equal<int>(nResult, 0);
760 }
761 finally
762 {
763 ReleaseVerutilVersion(pCopy);
764 ReleaseVerutilVersion(pSource);
765 }
766 }
767
768 [Fact]
769 void VerParseVersionTreatsTrailingDotsAsInvalid()
770 {
771 HRESULT hr = S_OK;
772 VERUTIL_VERSION* pVersion1 = NULL;
773 VERUTIL_VERSION* pVersion2 = NULL;
774 VERUTIL_VERSION* pVersion3 = NULL;
775 VERUTIL_VERSION* pVersion4 = NULL;
776 VERUTIL_VERSION* pVersion5 = NULL;
777 VERUTIL_VERSION* pVersion6 = NULL;
778 VERUTIL_VERSION* pVersion7 = NULL;
779 LPCWSTR wzVersion1 = L".";
780 LPCWSTR wzVersion2 = L"1.";
781 LPCWSTR wzVersion3 = L"2.1.";
782 LPCWSTR wzVersion4 = L"3.2.1.";
783 LPCWSTR wzVersion5 = L"4.3.2.1.";
784 LPCWSTR wzVersion6 = L"5-.";
785 LPCWSTR wzVersion7 = L"6-a.";
786
787 try
788 {
789 hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1);
790 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1);
791
792 hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2);
793 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2);
794
795 hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3);
796 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3);
797
798 hr = VerParseVersion(wzVersion4, 0, FALSE, &pVersion4);
799 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion4);
800
801 hr = VerParseVersion(wzVersion5, 0, FALSE, &pVersion5);
802 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion5);
803
804 hr = VerParseVersion(wzVersion6, 0, FALSE, &pVersion6);
805 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion6);
806
807 hr = VerParseVersion(wzVersion7, 0, FALSE, &pVersion7);
808 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion7);
809
810 NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion);
811 Assert::Equal<DWORD>(0, pVersion1->dwMajor);
812 Assert::Equal<DWORD>(0, pVersion1->dwMinor);
813 Assert::Equal<DWORD>(0, pVersion1->dwPatch);
814 Assert::Equal<DWORD>(0, pVersion1->dwRevision);
815 Assert::Equal<DWORD>(0, pVersion1->cReleaseLabels);
816 Assert::Equal<DWORD>(0, pVersion1->cchMetadataOffset);
817 Assert::Equal<BOOL>(TRUE, pVersion1->fInvalid);
818
819 NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion);
820 Assert::Equal<DWORD>(1, pVersion2->dwMajor);
821 Assert::Equal<DWORD>(0, pVersion2->dwMinor);
822 Assert::Equal<DWORD>(0, pVersion2->dwPatch);
823 Assert::Equal<DWORD>(0, pVersion2->dwRevision);
824 Assert::Equal<DWORD>(0, pVersion2->cReleaseLabels);
825 Assert::Equal<DWORD>(2, pVersion2->cchMetadataOffset);
826 Assert::Equal<BOOL>(TRUE, pVersion2->fInvalid);
827
828 NativeAssert::StringEqual(wzVersion3, pVersion3->sczVersion);
829 Assert::Equal<DWORD>(2, pVersion3->dwMajor);
830 Assert::Equal<DWORD>(1, pVersion3->dwMinor);
831 Assert::Equal<DWORD>(0, pVersion3->dwPatch);
832 Assert::Equal<DWORD>(0, pVersion3->dwRevision);
833 Assert::Equal<DWORD>(0, pVersion3->cReleaseLabels);
834 Assert::Equal<DWORD>(4, pVersion3->cchMetadataOffset);
835 Assert::Equal<BOOL>(TRUE, pVersion3->fInvalid);
836
837 NativeAssert::StringEqual(wzVersion4, pVersion4->sczVersion);
838 Assert::Equal<DWORD>(3, pVersion4->dwMajor);
839 Assert::Equal<DWORD>(2, pVersion4->dwMinor);
840 Assert::Equal<DWORD>(1, pVersion4->dwPatch);
841 Assert::Equal<DWORD>(0, pVersion4->dwRevision);
842 Assert::Equal<DWORD>(0, pVersion4->cReleaseLabels);
843 Assert::Equal<DWORD>(6, pVersion4->cchMetadataOffset);
844 Assert::Equal<BOOL>(TRUE, pVersion4->fInvalid);
845
846 NativeAssert::StringEqual(wzVersion5, pVersion5->sczVersion);
847 Assert::Equal<DWORD>(4, pVersion5->dwMajor);
848 Assert::Equal<DWORD>(3, pVersion5->dwMinor);
849 Assert::Equal<DWORD>(2, pVersion5->dwPatch);
850 Assert::Equal<DWORD>(1, pVersion5->dwRevision);
851 Assert::Equal<DWORD>(0, pVersion5->cReleaseLabels);
852 Assert::Equal<DWORD>(8, pVersion5->cchMetadataOffset);
853 Assert::Equal<BOOL>(TRUE, pVersion5->fInvalid);
854
855 NativeAssert::StringEqual(wzVersion6, pVersion6->sczVersion);
856 Assert::Equal<DWORD>(5, pVersion6->dwMajor);
857 Assert::Equal<DWORD>(0, pVersion6->dwMinor);
858 Assert::Equal<DWORD>(0, pVersion6->dwPatch);
859 Assert::Equal<DWORD>(0, pVersion6->dwRevision);
860 Assert::Equal<DWORD>(0, pVersion6->cReleaseLabels);
861 Assert::Equal<DWORD>(2, pVersion6->cchMetadataOffset);
862 Assert::Equal<BOOL>(TRUE, pVersion6->fInvalid);
863
864 NativeAssert::StringEqual(wzVersion7, pVersion7->sczVersion);
865 Assert::Equal<DWORD>(6, pVersion7->dwMajor);
866 Assert::Equal<DWORD>(0, pVersion7->dwMinor);
867 Assert::Equal<DWORD>(0, pVersion7->dwPatch);
868 Assert::Equal<DWORD>(0, pVersion7->dwRevision);
869 Assert::Equal<DWORD>(1, pVersion7->cReleaseLabels);
870
871 Assert::Equal<BOOL>(FALSE, pVersion7->rgReleaseLabels[0].fNumeric);
872 Assert::Equal<DWORD>(1, pVersion7->rgReleaseLabels[0].cchLabel);
873 Assert::Equal<DWORD>(2, pVersion7->rgReleaseLabels[0].cchLabelOffset);
874
875 Assert::Equal<DWORD>(4, pVersion7->cchMetadataOffset);
876 Assert::Equal<BOOL>(TRUE, pVersion7->fInvalid);
877 }
878 finally
879 {
880 ReleaseVerutilVersion(pVersion1);
881 ReleaseVerutilVersion(pVersion2);
882 ReleaseVerutilVersion(pVersion3);
883 ReleaseVerutilVersion(pVersion4);
884 ReleaseVerutilVersion(pVersion5);
885 ReleaseVerutilVersion(pVersion6);
886 ReleaseVerutilVersion(pVersion7);
887 }
888 }
889
890 [Fact]
891 void VerVersionFromQwordCreatesVersion()
892 {
893 HRESULT hr = S_OK;
894 VERUTIL_VERSION* pVersion1 = NULL;
895
896 try
897 {
898 hr = VerVersionFromQword(MAKEQWORDVERSION(1, 2, 3, 4), &pVersion1);
899 NativeAssert::Succeeded(hr, "VerVersionFromQword failed");
900
901 NativeAssert::StringEqual(L"1.2.3.4", pVersion1->sczVersion);
902 Assert::Equal<DWORD>(1, pVersion1->dwMajor);
903 Assert::Equal<DWORD>(2, pVersion1->dwMinor);
904 Assert::Equal<DWORD>(3, pVersion1->dwPatch);
905 Assert::Equal<DWORD>(4, pVersion1->dwRevision);
906 Assert::Equal<DWORD>(0, pVersion1->cReleaseLabels);
907 Assert::Equal<DWORD>(7, pVersion1->cchMetadataOffset);
908 Assert::Equal<BOOL>(FALSE, pVersion1->fInvalid);
909 }
910 finally
911 {
912 ReleaseVerutilVersion(pVersion1);
913 }
914 }
915
916 private:
917 void TestVerutilCompareParsedVersions(VERUTIL_VERSION* pVersion1, VERUTIL_VERSION* pVersion2, int nExpectedResult)
918 {
919 HRESULT hr = S_OK;
920 int nResult = 0;
921
922 hr = VerCompareParsedVersions(pVersion1, pVersion2, &nResult);
923 NativeAssert::Succeeded(hr, "Failed to compare versions '{0}' and '{1}'", pVersion1->sczVersion, pVersion2->sczVersion);
924
925 Assert::Equal(nExpectedResult, nResult);
926
927 hr = VerCompareParsedVersions(pVersion2, pVersion1, &nResult);
928 NativeAssert::Succeeded(hr, "Failed to compare versions '{0}' and '{1}'", pVersion1->sczVersion, pVersion2->sczVersion);
929
930 Assert::Equal(nExpectedResult, -nResult);
931 }
932 };
933}
diff --git a/src/libs/dutil/test/DUtilUnitTest/error.cpp b/src/libs/dutil/test/DUtilUnitTest/error.cpp
new file mode 100644
index 00000000..e51971c3
--- /dev/null
+++ b/src/libs/dutil/test/DUtilUnitTest/error.cpp
@@ -0,0 +1,26 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5const int ERROR_STRING_BUFFER = 1024;
6
7static char szMsg[ERROR_STRING_BUFFER];
8static WCHAR wzMsg[ERROR_STRING_BUFFER];
9
10void CALLBACK DutilTestTraceError(
11 __in_z LPCSTR /*szFile*/,
12 __in int /*iLine*/,
13 __in REPORT_LEVEL /*rl*/,
14 __in UINT source,
15 __in HRESULT hrError,
16 __in_z __format_string LPCSTR szFormat,
17 __in va_list args
18 )
19{
20 if (DUTIL_SOURCE_EXTERNAL == source)
21 {
22 ::StringCchPrintfA(szMsg, countof(szMsg), szFormat, args);
23 MultiByteToWideChar(CP_ACP, 0, szMsg, -1, wzMsg, countof(wzMsg));
24 throw gcnew System::Exception(System::String::Format("hr = 0x{0:X8}, message = {1}", hrError, gcnew System::String(wzMsg)));
25 }
26}
diff --git a/src/libs/dutil/test/DUtilUnitTest/error.h b/src/libs/dutil/test/DUtilUnitTest/error.h
new file mode 100644
index 00000000..b973acaf
--- /dev/null
+++ b/src/libs/dutil/test/DUtilUnitTest/error.h
@@ -0,0 +1,14 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4#define DUTIL_SOURCE_DEFAULT DUTIL_SOURCE_EXTERNAL
5
6void CALLBACK DutilTestTraceError(
7 __in_z LPCSTR szFile,
8 __in int iLine,
9 __in REPORT_LEVEL rl,
10 __in UINT source,
11 __in HRESULT hrError,
12 __in_z __format_string LPCSTR szFormat,
13 __in va_list args
14 );
diff --git a/src/libs/dutil/test/DUtilUnitTest/packages.config b/src/libs/dutil/test/DUtilUnitTest/packages.config
new file mode 100644
index 00000000..a4fef2bf
--- /dev/null
+++ b/src/libs/dutil/test/DUtilUnitTest/packages.config
@@ -0,0 +1,13 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3<packages>
4 <package id="xunit.abstractions" version="2.0.3" />
5 <package id="xunit.assert" version="2.4.1" />
6 <package id="xunit.core" version="2.4.1" />
7 <package id="xunit.extensibility.core" version="2.4.1" />
8 <package id="xunit.extensibility.execution" version="2.4.1" />
9 <package id="xunit.runner.msbuild" version="2.4.1" />
10 <package id="xunit.runner.visualstudio" version="2.4.1" />
11 <package id="WixBuildTools.TestSupport" version="4.0.47" />
12 <package id="WixBuildTools.TestSupport.Native" version="4.0.47" />
13</packages> \ No newline at end of file
diff --git a/src/libs/dutil/test/DUtilUnitTest/precomp.cpp b/src/libs/dutil/test/DUtilUnitTest/precomp.cpp
new file mode 100644
index 00000000..37664a1c
--- /dev/null
+++ b/src/libs/dutil/test/DUtilUnitTest/precomp.cpp
@@ -0,0 +1,3 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
diff --git a/src/libs/dutil/test/DUtilUnitTest/precomp.h b/src/libs/dutil/test/DUtilUnitTest/precomp.h
new file mode 100644
index 00000000..e9f8770b
--- /dev/null
+++ b/src/libs/dutil/test/DUtilUnitTest/precomp.h
@@ -0,0 +1,32 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#include <windows.h>
6#include <strsafe.h>
7#include <ShlObj.h>
8
9// Include error.h before dutil.h
10#include <dutilsources.h>
11#include "error.h"
12#include <dutil.h>
13
14#include <verutil.h>
15#include <atomutil.h>
16#include <dictutil.h>
17#include <dirutil.h>
18#include <fileutil.h>
19#include <guidutil.h>
20#include <iniutil.h>
21#include <memutil.h>
22#include <pathutil.h>
23#include <strutil.h>
24#include <monutil.h>
25#include <regutil.h>
26#include <rssutil.h>
27#include <apuputil.h> // NOTE: this must come after atomutil.h and rssutil.h since it uses them.
28#include <uriutil.h>
29#include <xmlutil.h>
30
31#pragma managed
32#include <vcclr.h>
diff --git a/src/libs/dutil/test/DUtilUnitTest/resource.h b/src/libs/dutil/test/DUtilUnitTest/resource.h
new file mode 100644
index 00000000..bdf252f6
--- /dev/null
+++ b/src/libs/dutil/test/DUtilUnitTest/resource.h
@@ -0,0 +1,2 @@
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